﻿using DocumentFormat.OpenXml;
using ICSharpCode.AvalonEdit.Highlighting;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp;
using MahApps.Metro.Controls;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Packaging;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Xps;
using System.Windows.Xps.Packaging;
using System.Xml;
using Image = System.Windows.Controls.Image;
using WinForm = System.Windows.Forms;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// MiniReportViewer.xaml 的交互逻辑
    /// </summary>
    public partial class MiniReportEditor : MetroWindow
    {
        /// <summary>
        /// 自定义高亮定义，完整定义。
        /// 但一千多行的帮助文档就明显延迟。
        /// </summary>
        private static IHighlightingDefinition reporterHighlighting;

        private static ImageSource noImageSource;
        /// <summary>
        /// 图像部件找不到指示的图像时使用的占位符图像。
        /// </summary>
        public static ImageSource NoImageSource { get { return noImageSource; } }

        private static Converter.ModifiedToText modifiedToTextConverter = new LunarMarkdownEditor.Converter.ModifiedToText();
        /// <summary>
        /// [只读]bool to "已修改 未修改" 的字符串的转换器。
        /// </summary>
        public static Converter.ModifiedToText ModifiedToTextConverter { get { return modifiedToTextConverter; } }

        static MiniReportEditor()
        {
            Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.Reporter.xshd");
            if (s != null)
            {
                using (XmlReader reader = new XmlTextReader(s))
                {
                    reporterHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }

            noImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/PlaceHolder.png"));
        }

        public MiniReportEditor(string reporterScript, bool readonlyMode = false)
        {
            InitializeComponent();

            if (readonlyMode)
            {
                fullLayout = true;
                cdTools2.Width =
                    cdTools1.Width = new GridLength(0);
                mainDocumentViewer.Focus();
            }
            else
            {
                fullLayout = false;
                cdTools1.Width = new GridLength(0, GridUnitType.Auto);
                cdTools2.Width = new GridLength(2, GridUnitType.Star);
                editorBase.Focus();
            }

            // 编辑器属性设置
            HighlightingManager.Instance.RegisterHighlighting("Reporter Highlighting", new string[] { ".py" }, reporterHighlighting);
            editorBase.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".py");

            // 载入脚本生成报表，这是Editor，不应该调用这个方法
            // LoadReportAndFillFirstRecord(reporterScript, dataContextTable);

            findAndReplaceDialog = new FindReplaceDialog(editorBase);

            editorBase.EnableAutoCompleteChanged += EditorBase_EnableAutoCompleteChanged;
            this.PreviewTextInput += PythonEditor_PreviewTextInput;
            this.PreviewKeyDown += MiniReportEditor_PreviewKeyDown;

            editorBase.Text = string.IsNullOrWhiteSpace(reporterScript) ? "" : reporterScript;
            editorBase.IsModified = false;

            var config = App.WorkspaceConfigManager.Get("ReporterEditSwitchLayout");
            if (bool.TryParse(config, out bool result))
            {
                switchEditorAndPreviewer = result;
            }
            else switchEditorAndPreviewer = false;

            SwitchEditorAndPreviewer(switchEditorAndPreviewer);

            var sPath = App.WorkspaceConfigManager.Get("ReporterScriptFilePath");
            if (File.Exists(sPath))
            {
                editorBase.Text = File.ReadAllText(sPath);
                editorBase.IsModified = false;
                tbMsg.Text = scriptFullPath = sPath;
                tbMsg.ToolTip = "鼠标左键双击复制文件";
            }

            editorBase.MasterReportEditor = this;
        }

        private string scriptFullPath = "";

        private bool fullLayout = false;

        private bool switchEditorAndPreviewer = false;

        private List<ReportHelpPathInfo> reporthelp_path_list = new List<ReportHelpPathInfo>();

        private void MiniReportEditor_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            // 这几个快捷键都是全窗口级别的。
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            switch (e.Key)
            {
                case Key.Escape:
                    {
                        if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.IsVisible)
                        {
                            this.editorBase.CompletionWindow.Hide();
                        }
                        e.Handled = true;
                        break;
                    }
                case Key.S:
                    {
                        if (isCtrl && !isAlt)
                        {
                            if (isShift)
                            {
                                SaveReporterScriptAs();
                            }
                            else
                            {
                                SaveReporterScript();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.O:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            btnLoadReporterScript_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F1:
                    {
                        if (isCtrl)
                        {
                            if (rdDataGridArea.ActualHeight > 10)
                            {
                                rdDataGridArea.Height = new GridLength(0, GridUnitType.Star);
                            }
                            else
                            {
                                rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
                            }
                        }
                        else
                        {
                            // 尝试显示帮助文档

                            ShowHelpDocumentPage();
                        }

                        e.Handled = true;
                        break;
                    }
                case Key.F2:
                    {
                        miInsertCompsiteBrackts_Click(sender, e);
                        break;
                    }
                case Key.F3:
                    {
                        miInsertCompsiteLogicBrackts_Click(sender, e);
                        break;
                    }
                case Key.F4:
                    {
                        miInsertCompsiteCustomLogicBrackts_Click(sender, e);
                        break;
                    }
                case Key.F5:
                    {
                        // LoadReportAndFillFirstRecord(dataContextTable);
                        btnPreviewReporter_Click(sender, e);
                        e.Handled = true;
                        break;
                    }
                case Key.F11:
                    {
                        // Ctrl 窗口最大化在下面的方法中判断
                        SwitchFullLayout();
                        break;
                    }
                case Key.F12:
                    {
                        switchEditorAndPreviewer = !switchEditorAndPreviewer;
                        SwitchEditorAndPreviewer(switchEditorAndPreviewer);
                        break;
                    }
                case Key.H:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            Replace();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            Find();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.R:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miAutoWrap_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            // Ctrl + D 相当于 Shift + Delete
                            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
                            if (line != null)
                            {
                                this.editorBase.Select(line.Offset, 0);
                                this.editorBase.Document.Replace(line.Offset, line.Length + line.DelimiterLength, "");
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.D0:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length - 1, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D1:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，1）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，1）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D2:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，2）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，2）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D3:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，3）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，3）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D4:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，4）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，4）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D5:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，5）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，5）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D6:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，6）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，6）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D7:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，7）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，7）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D8:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，8）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，8）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D9:
                    {
                        string spanTxt;
                        if (isCtrl)
                        {
                            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
                            if (!isAlt)
                            {
                                spanTxt = "\r\n；新文本：（_，_，1，9）";
                            }
                            else
                            {
                                spanTxt = "\r\n；新文本字段：（_，_，1，9）";
                            }
                            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
                            editorBase.Select(curLine.EndOffset + spanTxt.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.Delete:
                    {
                        if (!isCtrl && isShift && !isAlt)
                        {
                            // Shift + Delete
                            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
                            if (line != null)
                            {
                                this.editorBase.Select(line.Offset, 0);
                                this.editorBase.Document.Replace(line.Offset, line.Length + line.DelimiterLength, "");
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.Z:
                    {
                        if (isCtrl && isAlt)
                        {
                            if (isShift)
                            {
                                if (Globals.MainWindow.SupportFolding == false) return;

                                this.editorBase.FoldSelectedBlock();
                            }
                            else
                            {
                                if (Globals.MainWindow.SupportFolding == false) return;

                                foreach (var i in this.editorBase.FoldingManager.AllFoldings)
                                {
                                    i.IsFolded = true;
                                }
                            }
                            e.Handled = true;
                        }
                        break;
                    }
            }
        }

        private void ShowHelpDocumentPage()
        {
            if (reporthelp_path_list == null)
                reporthelp_path_list = new List<ReportHelpPathInfo>();

            if (reporthelp_path_list.Count <= 0)
            {
                var reporthelp_paths = Properties.Resources.reporthelp_paths.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var line in reporthelp_paths)
                {
                    var spans = line.Split(new char[] { '：', ':' }, StringSplitOptions.RemoveEmptyEntries);
                    if (spans.Length == 2)
                    {
                        reporthelp_path_list.Add(new ReportHelpPathInfo()
                        {
                            CommandName = spans[0],
                            HelpRelativePath = spans[1],
                        });
                    }
                }
            }

            var dLine = this.EditorBase.Document.GetLineByOffset(this.EditorBase.SelectionStart);
            if (dLine != null)
            {
                var lineTxt = this.EditorBase.Document.GetText(dLine.Offset, dLine.Length);
                var cmd = ReporterDefinition.GetCommand(lineTxt);
                if (cmd == null)
                {
                    ShowHelpPage(null);
                }
                else
                {
                    if (string.IsNullOrWhiteSpace(cmd.CommandValue) == true)
                    {
                        ShowHelpPage(null);
                    }
                    else
                    {
                        ShowHelpPage(cmd.CommandName);
                    }
                }
            }
        }


        /// <summary>
        /// 要调用本方法，必须保证帮助文档中存在对应名称的页面。
        /// </summary>
        /// <param name="helpKeyWord">帮助文档中某页面的ID。</param>
        /// <returns></returns>
        public string ShowHelpPage(string helpKeyWord)
        {
            string helpFilePath = Globals.InstalledPath;
            if (helpFilePath.EndsWith("\\") == false)
            {
                helpFilePath += "\\";
            }

            helpFilePath += "reporthelp.chm";

            if (System.IO.File.Exists(helpFilePath) == false)
            {
                return "　　未找到 reporthelp.chm 文件，无法继续！";
            }

            if (helpKeyWord == null || helpKeyWord.Length <= 0)
            {
                WinForm.Help.ShowHelp(null, helpFilePath);
                return "";
            }

            string htmlString = null;

            foreach (var s in reporthelp_path_list)
            {
                if (s.CommandName.ToLower() == helpKeyWord.ToLower())
                {
                    htmlString = s.HelpRelativePath;
                    break;
                }
            }

            try
            {
                if (string.IsNullOrEmpty(htmlString) == false)
                {
                    WinForm.Help.ShowHelp(null, helpFilePath,
                         WinForm.HelpNavigator.Topic, htmlString);
                }
                else
                {
                    WinForm.Help.ShowHelp(null, helpFilePath);
                }

                return string.Empty;
            }
            catch (Exception ex)
            {
                return "　　发生意外错误，不能顺利打开帮助文档！\r\n" +
                     "异常信息如下：\r\n" + ex.Message + "\r\n" + ex.StackTrace;
            }
        }


        private void SwitchFullLayout()
        {
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            if (fullLayout)
            {
                fullLayout = false;
                rdMain.Height = new GridLength(3, GridUnitType.Star);
                rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
                rdStatus.Height = new GridLength(1, GridUnitType.Auto);

                cdMainArea.Width = new GridLength(3, GridUnitType.Star);
                cdTools1.Width = new GridLength(1, GridUnitType.Auto);
                cdTools2.Width = new GridLength(2, GridUnitType.Star);
            }
            else
            {
                fullLayout = true;
                if (isCtrl)
                {
                    this.WindowState = WindowState.Maximized;
                }
                rdMain.Height = new GridLength(1, GridUnitType.Star);
                rdDataGridArea.Height = new GridLength(0, GridUnitType.Star);
                rdStatus.Height = new GridLength(0, GridUnitType.Star);

                cdMainArea.Width = new GridLength(1, GridUnitType.Star);
                cdTools1.Width = new GridLength(0, GridUnitType.Star);
                cdTools2.Width = new GridLength(0, GridUnitType.Star);
            }
        }

        private void SwitchEditorAndPreviewer(bool switcher)
        {
            if (switcher)
            {
                Grid.SetColumn(mainDocumentViewer, 2);
                Grid.SetColumn(editorBase, 0);
                Grid.SetColumn(btnPreview, 2);

                mainDocumentViewer.Margin = new Thickness(5, 10, 10, 10);
                editorBase.Margin = new Thickness(10, 10, 5, 10);
                btnPreview.Margin = new Thickness(0, 14, 13, 0);
            }
            else
            {
                Grid.SetColumn(mainDocumentViewer, 0);
                Grid.SetColumn(editorBase, 2);
                Grid.SetColumn(btnPreview, 0);

                mainDocumentViewer.Margin = new Thickness(10, 10, 5, 10);
                editorBase.Margin = new Thickness(5, 10, 10, 10);
                btnPreview.Margin = new Thickness(0, 14, 8, 0);
            }

            App.WorkspaceConfigManager.Set("ReporterEditSwitchLayout", switchEditorAndPreviewer.ToString());
        }

        private void Find()
        {
            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.tabMain.SelectedIndex = 0;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            if (!editorBase.TextArea.Selection.IsMultiline)
            {
                findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.Focus();
            }

            if (editorBase.IsReadOnly)
            {
                findAndReplaceDialog.IsReplacePanelVisible = false;
            }
            else findAndReplaceDialog.IsReplacePanelVisible = true;
        }

        private void Replace()
        {
            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.tabMain.SelectedIndex = 1;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            if (editorBase.IsReadOnly)
            {
                findAndReplaceDialog.IsReplacePanelVisible = false;

                if (!editorBase.TextArea.Selection.IsMultiline)
                {
                    findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind.Focus();
                }
            }
            else
            {
                findAndReplaceDialog.IsReplacePanelVisible = true;

                if (!editorBase.TextArea.Selection.IsMultiline)
                {
                    findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind2.SelectAll();
                    findAndReplaceDialog.txtFind2.Focus();
                }
            }

        }

        private void PythonEditor_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            // 当输入法处于中文状态时，PreviewKeyDown 事件得到的总是 Key.ImeProcessed，所以要在这里处理 = 号输入的情况。

            if (e.Text == "=")
            {
                // 这几个快捷键都是全窗口级别的。
                KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
                KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
                KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
                KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
                KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
                KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

                bool isCtrl, isShift, isAlt;

                isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
                isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
                isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

                if (isCtrl || isShift || isAlt) return;

                var leftTxt = this.editorBase.LeftText;

                if (leftTxt.EndsWith(" /"))
                {
                    this.editorBase.Document.Replace(this.editorBase.SelectionStart, 0, "/ ");
                    if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.Visibility == Visibility.Visible)
                    {
                        this.editorBase.CompletionWindow.Close();
                    }
                    e.Handled = true;
                }
            }
        }

        public ReporterEditBase EditorBase { get { return editorBase; } }

        private string fullPath = "";
        /// <summary>
        /// [只读]Python 脚本文件的磁盘路径。
        /// </summary>
        public string FullPath { get { return this.fullPath; } }


        public void EditorBase_EnableAutoCompleteChanged(object sender, EnableAutoCompleteEventArgs e)
        {
            if (e.NewValue)
            {
                tbEnableAutoComplete.Text = "【自动提示】";
                spEnableAutoComplete.Background = System.Windows.Media.Brushes.White;
                tbEnableAutoComplete.Foreground = dpStatus.Background;
            }
            else
            {
                tbEnableAutoComplete.Text = "【禁用提示】";
                spEnableAutoComplete.Background = System.Windows.Media.Brushes.Transparent;
                tbEnableAutoComplete.Foreground = System.Windows.Media.Brushes.White;
            }
        }

        private FindReplaceDialog findAndReplaceDialog;

        public void LoadReportAndFillFirstRecord(文字表 dataContext)
        {
            this.dataContextTable = dataContext;

            if (dataContext.文字表头行集 != null && dataContext.文字表头行集.Count > 0 &&
                dataContext.文字表头行集[0].文字表单元格集 != null &&
                dataContext.文字表头行集[0].文字表单元格集.Count > 0)
            {
                var dt = new DataTable();
                var headRow = dataContext.文字表头行集[0];
                foreach (var head in headRow.文字表单元格集)
                {
                    dt.Columns.Add(head.文字表单元格文本, typeof(string));
                }

                if (dataContext.文字表体行集 != null && dataContext.文字表体行集.Count > 0)
                {
                    foreach (var row in dataContext.文字表体行集)
                    {
                        var newRow = dt.NewRow();
                        if (row.文字表单元格集.Count == headRow.文字表单元格集.Count)
                        {
                            for (int i = 0; i < row.文字表单元格集.Count; i++)
                            {
                                newRow[i] = row.文字表单元格集[i].文字表单元格文本;
                            }
                        }
                        dt.Rows.Add(newRow);
                    }
                }

                dataGrid.ItemsSource = dt.DefaultView;
            }
        }

        public void LoacReportAndFillFirstRecord(string reporterScriptFilePath, string reporterData2dTableFilePath)
        {
            try
            {
                var script = File.ReadAllText(reporterScriptFilePath);
                var textTable = new 文字表(File.ReadAllText(reporterData2dTableFilePath));

                LoadReportAndFillFirstRecord(dataContextTable);

                this.fullPath = reporterScriptFilePath;
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private 文字表 dataContextTable;

        public 文字表 DataContextTable { get => dataContextTable; }

        public static DoubleCollection GetDashStyle(string dashMark)
        {
            if (string.IsNullOrWhiteSpace(dashMark)) return null;

            switch (dashMark.Replace("=", "_").Replace("-", "_").Replace("。", ".").Replace("—", "_"))
            {
                case ".":
                case "点":
                case "点线":
                    {
                        //return DashStyles.Dot.Dashes; // 不显示 DashStyles.Dot.Dashes;
                        return new DoubleCollection() { 0, 2 };
                    }
                case "_":
                case "划":
                case "划线":
                    {
                        //return DashStyles.Dash.Dashes;
                        return new DoubleCollection() { 2, 2 };
                    }
                case "._":
                case "_.":
                case "点划":
                case "划点":
                case "点划线":
                case "划点线":
                    {
                        // return DashStyles.DashDot.Dashes;
                        return new DoubleCollection() { 2, 2, 0, 2 };
                    }
                case ".._":
                case "_..":
                case "点点划":
                case "点点划线":
                case "划点点":
                case "划点点线":
                    {
                        // return DashStyles.DashDotDot.Dashes;
                        return new DoubleCollection() { 2, 2, 0, 2, 0, 2 };
                    }

                default: return DashStyles.Solid.Dashes;
            }
        }

        /// <summary>
        /// 将 pt 字号转换为 DIU 字号。
        /// </summary>
        /// <param name="ptValue"></param>
        /// <returns></returns>
        public static double? ConvertPtToDiu(string ptValue)
        {
            FontSizeConverter converter = new FontSizeConverter();
            try
            {
                if (string.IsNullOrWhiteSpace(ptValue)) return null;

                if (ptValue.ToLower().EndsWith("pt"))
                {
                    return (double)converter.ConvertFromInvariantString(ptValue);
                }
                else
                {
                    return (double)converter.ConvertFromInvariantString($"{ptValue}pt");
                }
            }
            catch
            {
                return null;
            }
        }

        public static Regex RegCodeImageType { get; } = new Regex(@"[\[【［][\da-zA-Z０-９]*[\]】］]");

        /// <summary>
        /// 页面背景部件的层级索引。
        /// </summary>
        public static int BackgroundZIndex { get; set; } = 0;  // 背景图

        /// <summary>
        /// 在版芯位置的辅助线使用的层级索引。
        /// </summary>
        public static int AssistLineZIndex { get; } = 100;         // 辅助线在除背景图之外的最底层

        /// <summary>
        /// 在版芯的网格部件使用的层级索引。
        /// </summary>
        public static int BackgroundGridZIndex { get; } = 200;    // 网格在底层时（仅作网格线）

        /// <summary>
        /// 在版芯的内容部件使用的层级索引。
        /// </summary>
        public static int ContentWidgetsZIndex { get; } = 300;          // 内容部件

        /// <summary>
        /// 在版芯的装饰线部件使用的层级索引（横线、纵向、斜线使用）。
        /// </summary>
        public static int AdornerWidgetsZIndex { get; } = 400;          // 装饰线

        /// <summary>
        /// 网格仅作外框时的层级索引。
        /// </summary>
        public static int ForegroundGridZIndex { get; } = 500;    // 网格在最表层时（仅作外框）

        /// <summary>
        /// 页眉页脚和左右边栏线型部件使用的层级索引。
        /// 注意：与版芯相反，这里装饰线在内容部件下层。以便实现类似线上带五角星这样的效果。
        /// </summary>
        public static int OutBorderLineWidgetsZIndex { get; } = 600;

        /// <summary>
        /// 页眉页脚和左右边栏内容部件使用的层级索引。
        /// 注意：与在版芯相反，这里内容部件在装饰线上层。以便实现类似线上带五角星这样的效果。
        /// </summary>
        public static int OutBorderContentWidgetsZIndex { get; } = 700;      // 页眉页脚和左右边栏

        private System.Windows.Media.Brush errForeColor = System.Windows.Media.Brushes.Red;

        public System.Windows.Media.Brush ErrForeColor
        {
            get
            {
                if (errForeColor == null)
                {
                    return System.Windows.Media.Brushes.Red;
                }
                else return this.errForeColor;
            }
            set { this.errForeColor = value; }
        }

        public List<IFieldUIElement> BuildReporterByScript(int rowIndex, FixedDocument doc, string reporterScript,
                                                           out ReporterDefinition rd, bool drawAssistLines)
        {
            // 解析报表生成脚本
            rd = new ReporterDefinition(reporterScript);
            try
            {
                rd.BuildReportModel();
            }
            catch (ReportScriptCompileExpection ex)
            {
                var errLine = editorBase.Document.GetLineByNumber(ex.LineNumber);
                editorBase.Select(errLine.Offset, errLine.Length);
                editorBase.ScrollToLine(errLine.LineNumber);
                LMessageBox.ShowWarning(ex.Message);
                return null;
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
                return null;
            }

            if (rd.Pages.Count == 0) return null;

            if (rd.AssistLinesVisible != null && rd.AssistLinesVisible.HasValue)
            {
                // 用户设置了“显示辅助线”命令
                // 此时以用户设置为准，忽略界面设置

                drawAssistLines = rd.AssistLinesVisible.Value;
                ckxPreviewWithAssistLines.IsChecked = drawAssistLines;  // 更新界面复选框勾选状态
            }
            //else{...}  不变，以传入的参数值（通常就是界面上“预览时显示辅助线”复选框的勾选状态）为准。

            defaultImagesPath = rd.DefaultImagesPath;
            defaultFieldImagesPath = rd.DefaultFieldImagesPath;

            // 生成报表文档
            // 可填充数据的字段部件列表
            var fieldElements = new List<IFieldUIElement>();

            // 默认属性
            var defPageBackground = BrushManager.GetBrush(rd.DefaultPageBackground);
            if (defPageBackground == null)
                defPageBackground = System.Windows.Media.Brushes.White;

            var defBackground = BrushManager.GetBrush(rd.DefaultBackground);
            if (defBackground == null)
                defBackground = System.Windows.Media.Brushes.White;

            var defBoxBackground = BrushManager.GetBrush(rd.DefaultBoxBackground);  // 外框部件专用默认背景色
            if (defBoxBackground == null)
                defBoxBackground = System.Windows.Media.Brushes.Transparent;

            var defForeground = BrushManager.GetBrush(rd.DefaultForeground);
            if (defForeground == null)
                defForeground = System.Windows.Media.Brushes.Black;

            var defLineColor = BrushManager.GetBrush(rd.DefaultLineColor);
            if (defLineColor == null)
                defLineColor = System.Windows.Media.Brushes.Black;

            var defLineWidth = rd.DefaultLineWidth;

            var defLineDashType = GetDashStyle(rd.DefaultLineDashType);
            if (defLineDashType == null)
                defLineDashType = DashStyles.Solid.Dashes;

            var defBoxColor = BrushManager.GetBrush(rd.DefaultBoxLineColor);
            if (defBoxColor == null)
                defBoxColor = System.Windows.Media.Brushes.Black;

            var defBoxLineWidth = rd.DefaultBoxLineWidth;

            var defBoxDashType = GetDashStyle(rd.DefaultBoxLineDashType);
            if (defBoxDashType == null)
                defBoxDashType = DashStyles.Solid.Dashes;

            var errMessageForeColor = BrushManager.GetBrush(rd.ErrorMessageForeground);
            if (errMessageForeColor == null)
            {
                errMessageForeColor = System.Windows.Media.Brushes.Red;
            }
            this.errForeColor = errMessageForeColor;  // 默认错误消息前景色彩

            var warningMsg = new StringBuilder();

            for (int pageIndex = 0; pageIndex < rd.Pages.Count; pageIndex++)
            {
                var page = rd.Pages[pageIndex];
                var pc = new PageContent();
                var size = page.PageSize;
                pc.Width = size.Width;
                pc.Height = size.Height;

                #region 根据上/上/左/右四边边距和辅助线宽，计算出最大行、列数目并提供警告信息
                var curRowsCount = page.PageRowsCount;
                var curColumnsCount = page.PageColumnsCount;
                var leftMargin = page.LeftMargin;
                var rightMargin = page.RightMargin;
                var topMargin = page.TopMargin;
                var bottomMargin = page.BottomMargin;
                var assistLineWidth = rd.AssistLinesWidth;

                var hSpace = size.Width - leftMargin - rightMargin;
                var maxColumnsCount = (int)hSpace / (assistLineWidth + 10);
                var vSpace = size.Height - topMargin - rightMargin;
                var maxRowsCount = (int)vSpace / (assistLineWidth + 20);

                if (curRowsCount > maxRowsCount)
                {
                    warningMsg.Append($"根据当前页面上下边距和辅助线宽度计算，第 {pageIndex + 1} 页最好不要超过 {maxRowsCount} 行。当前设置的是 {curRowsCount} 行，可能导致页面拥挤、错位。");
                }

                if (curColumnsCount > maxColumnsCount)
                {
                    warningMsg.Append($"根据当前页面左右边距和辅助线宽度计算，第 {pageIndex + 1} 页最好不要超过 {maxColumnsCount} 列。当前设置的是 {curColumnsCount} 列，可能导致页面拥挤、错位。");
                }
                #endregion

                page.RefreshLocation(rd.AssistLinesWidth);

                var fixedPage = new FixedPage() { Width = size.Width, Height = size.Height, };

                // 向 fixedPage 中添加内容
                var mainCanvas = new Canvas()
                {
                    Width = size.Width,
                    Height = size.Height,
                    // 背景色后面会设置
                };
                fixedPage.Children.Add(mainCanvas);

                // mainCanvas 承载所有内容

                var pageBackgroundBrush = BrushManager.GetBrush(page.PageBackground);
                if (pageBackgroundBrush != null)
                    mainCanvas.Background = pageBackgroundBrush;
                else mainCanvas.Background = defPageBackground;

                if (drawAssistLines)  // 绘制辅助线
                {
                    var assistLinesBrush = BrushManager.GetBrush(rd.AssistLinesColor);
                    if (assistLinesBrush == null)
                    {
                        assistLinesBrush = System.Windows.Media.Brushes.SkyBlue;
                    }

                    var assistLinesWidth = rd.AssistLinesWidth;  // 辅助线宽度至少为 1
                    var assistDashArray = GetDashStyle(rd.AssistLinesType);
                    if (assistDashArray == null)
                        assistDashArray = DashStyles.Solid.Dashes;   // 未设置视为实线（辅助线型不允许为空，但标记可能为空）

                    var outboxOfPage = new System.Windows.Shapes.Rectangle()
                    {
                        Width = page.ActualContentWidth + assistLinesWidth * 2 - assistLinesWidth,
                        Height = page.ActualContentHeight + assistLinesWidth * 2 - assistLinesWidth,
                        Stroke = assistLinesBrush,
                        StrokeThickness = assistLinesWidth,
                        StrokeDashArray = assistDashArray,
                        StrokeEndLineCap = PenLineCap.Flat,
                        StrokeStartLineCap = PenLineCap.Flat,
                        StrokeLineJoin = PenLineJoin.Miter,
                        StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                        SnapsToDevicePixels = true,
                    };
                    mainCanvas.Children.Add(outboxOfPage);
                    Canvas.SetZIndex(outboxOfPage, AssistLineZIndex);                // 辅助线、框均在底层。
                    // 层级关系到内容部件能否“覆盖”掉辅助线、装饰线条部件等。
                    Canvas.SetLeft(outboxOfPage, page.LeftTopPoint.X);
                    Canvas.SetTop(outboxOfPage, page.LeftTopPoint.Y);

                    for (int i = 0; i < page.PageColumnsCount - 1; i++)
                    {
                        // 竖线
                        var x = page.LeftTopPoint.X + (page.CellWidth + assistLinesWidth) * (i + 1);
                        var y1 = page.LeftTopPoint.Y + rd.AssistLinesWidth;
                        var y2 = page.LeftTopPoint.Y + page.PageRowsCount * (page.CellHeight + rd.AssistLinesWidth);

                        var line = new System.Windows.Shapes.Line()          // WPF Line 对象的坐标很奇怪，不如用 Rectangle 代替！
                        {
                            //Width = assistLinesWidth,
                            //Height = page.RightBottomPoint.Y - page.LeftTopPoint.Y + assistLinesWidth,
                            Y1 = y1,
                            Y2 = y2,
                            Stroke = assistLinesBrush,
                            StrokeThickness = assistLinesWidth,
                            StrokeDashArray = assistDashArray,
                            StrokeEndLineCap = PenLineCap.Flat,
                            StrokeStartLineCap = PenLineCap.Flat,
                            StrokeLineJoin = PenLineJoin.Miter,
                            StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                            SnapsToDevicePixels = true,
                        };

                        if (assistLinesWidth % 2 == 1)
                        {
                            line.X2 = line.X1 = x + assistLinesWidth / 2 + 0.5;
                        }
                        else
                        {
                            line.X2 = line.X1 = x + assistLinesWidth / 2;
                        }

                        mainCanvas.Children.Add(line);
                        //Canvas.SetLeft(line, x);
                        //Canvas.SetTop(line, page.LeftTopPoint.Y);
                        Canvas.SetZIndex(line, AssistLineZIndex);           // 辅助线在底层
                    }

                    for (int i = 0; i < page.PageRowsCount - 1; i++)
                    {
                        // 横线
                        var y = page.LeftTopPoint.Y + (page.CellHeight + assistLinesWidth) * (i + 1);
                        var x1 = page.LeftTopPoint.X + rd.AssistLinesWidth;
                        var x2 = page.LeftTopPoint.X + page.PageColumnsCount * (page.CellWidth + rd.AssistLinesWidth);

                        var line = new System.Windows.Shapes.Line()          // WPF Line 对象的坐标很奇怪，不如用 Rectangle 代替！
                        {
                            //Height = assistLinesWidth,
                            //Width = page.RightBottomPoint.X - page.LeftTopPoint.X,
                            X1 = x1,
                            X2 = x2,
                            Stroke = assistLinesBrush,
                            StrokeThickness = assistLinesWidth,
                            StrokeDashArray = assistDashArray,
                            StrokeEndLineCap = PenLineCap.Flat,
                            StrokeStartLineCap = PenLineCap.Flat,
                            StrokeLineJoin = PenLineJoin.Miter,
                            StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                            SnapsToDevicePixels = true,
                        };

                        if (assistLinesWidth % 2 == 1)
                        {
                            line.Y2 = line.Y1 = y + assistLinesWidth / 2 + 0.5;
                        }
                        else
                        {
                            line.Y2 = line.Y1 = y + assistLinesWidth / 2;
                        }

                        mainCanvas.Children.Add(line);
                        //Canvas.SetLeft(line, page.LeftTopPoint.X + rd.AssistLinesWidth);
                        //Canvas.SetTop(line, y);
                        Canvas.SetZIndex(line, AssistLineZIndex);           // 辅助线在底层
                    }
                }

                BuildWidgets(rowIndex, 1, rd, fieldElements, defBackground,
                             defBoxBackground, defForeground, defLineColor,
                             defLineDashType, defBoxColor, defBoxDashType,
                             defaultImagesPath, defaultFieldImagesPath,
                             warningMsg, pageIndex, page, mainCanvas, rd.Widgets,
                             out string idWidgetAreasText_inBar,             // 考试号填涂区域位置信息（实际不使用）
                             out string questionWidgetAreasText_inBar,       // 试题答案填涂区域位置信息（实际不使用）
                             out string answerBoxBorderWidthRateText_inBar,
                             out string answerBoxBorderHeightRateText_inBar);               // 边四栏重复部件。

                this.IdWidgetAreasText_inBar = idWidgetAreasText_inBar;
                this.QuestionWidgetAreasText_inBar = questionWidgetAreasText_inBar;

                BuildWidgets(rowIndex, 1, rd, fieldElements, defBackground,
                             defBoxBackground, defForeground, defLineColor,
                             defLineDashType, defBoxColor, defBoxDashType,
                             defaultImagesPath, defaultFieldImagesPath,
                             warningMsg, pageIndex, page, mainCanvas, page.Widgets,
                             out string idWidgetAreasText,             // 考试号填涂区域位置信息
                             out string questionWidgetAreasText,       // 试题答案填涂区域位置信息
                             out string answerBoxBorderWidthRateText,
                             out string answerBoxBorderHeightRateText);         // 只在单个页面中出现的部件

                this.IdWidgetAreasText = idWidgetAreasText;
                this.QuestionWidgetAreasText = questionWidgetAreasText;
                this.AnswerBoxBorderWidthRate = answerBoxBorderWidthRateText;
                this.AnswerBoxBorderHeightRate = answerBoxBorderHeightRateText;

                ((IAddChild)pc).AddChild(fixedPage);
                doc.Pages.Add(pc);
                mainCanvas.InvalidateVisual();
            }

            if (warningMsg.Length > 0)
            {
                LMessageBox.ShowWarning(warningMsg.ToString());
            }

            return fieldElements;
        }

        public static void BuildWidgets(int groupIndex,    // group 是指报表模板中所有页面组成的一个 Group；groupIndex 是为了计算页码。
                                        int rowsCount,     // rowsCount 是填充的记录的数量为了计算所有页面的数量
                                        ReporterDefinition rd, List<IFieldUIElement> fieldElements,
                                        System.Windows.Media.Brush defBackground,
                                        System.Windows.Media.Brush defBoxBackground,
                                        System.Windows.Media.Brush defForeground,
                                        System.Windows.Media.Brush defLineColor,
                                        DoubleCollection defLineDashType,
                                        System.Windows.Media.Brush defBoxColor,
                                        DoubleCollection defBoxDashType,
                                        string defaultImagesPath,
                                        string defaultFieldImagesPath,
                                        StringBuilder warningMsg,
                                        int pageIndex, ReporterPage page,
                                        Canvas mainCanvas,
                                        List<ReporterWidget> widgets,
                                        out string idWidgetAreasText,          // 输出考试号填涂区域位置
                                        out string questionWidgetAreasText,    // 输出试题填涂区域位置
                                        out string answerBoxBorderWidthRateText,        // 边框宽度比
                                        out string answerBoxBorderHeightRateText        // 边框高度比
            )
        {
            // 取边栏区域开关信息
            var barSwitcher = new bool[12];
            for (int i = 0; i < 12; i++)
            {
                barSwitcher[i] = page.BarSwitcher[i].HasValue ? page.BarSwitcher[i].Value : rd.DefaultBarSwitcher[i];
            }


            var baseBorderWidth = 0;   // 答题卡识别时需要知道每道题每个选项的位置，而这个位置与外框线宽度的一半相关
            foreach (var w in widgets)
            {
                var bw = w as ReporterBoxWidget;
                if (w == null) continue;

                if (w.Adorner)
                {
                    baseBorderWidth = w.BorderWidth.HasValue ? w.BorderWidth.Value / 2 : 0;
                    break;
                }
            }

            var idWidgetAreas = new StringBuilder();            // 答题卡考试号填涂区域位置信息
            var questionWidgetAreas = new StringBuilder();      // 答题卡试题部件填涂区域位置信息
            var questionWidgetFillChars = new StringBuilder();  // 答题卡上各试题的应填涂字符集信息，以管道字符 | 分割
            var answerSheetBoxBorderWidthRate = new StringBuilder();  // 答题卡边框宽度比
            var answerSheetBoxBorderHeightRate = new StringBuilder();  // 答题卡边框高度比
            var idNumber = 0;         // 答题卡才会用到，自动计算考试号的数位
            var questionsNumber = 0;  // 答题卡才会用到，自动计算题号

            for (int widgetIndex = 0; widgetIndex < widgets.Count; widgetIndex++)
            {
                var widget = widgets[widgetIndex];
                WidgetDisplayMode displayMode;
                if (widget.BarDisplay != null && widget.BarDisplay.HasValue)
                {
                    displayMode = widget.BarDisplay.Value;
                }
                else
                {
                    displayMode = rd.DefaultBarWidgetDisplayMode;
                }

                var pageNumber = pageIndex + 1;
                switch (displayMode)
                {
                    case WidgetDisplayMode.奇数页:
                        {
                            if (pageNumber % 2 == 0) continue;  // 偶数页不显示
                            break;
                        }
                    case WidgetDisplayMode.偶数页:
                        {
                            if (pageNumber % 2 == 1) continue;  // 奇数页不显示
                            break;
                        }
                        //case WidgetDisplayMode.全部页: { break; } // 这种没必要判断
                }

                // 根据边栏开关判断是否需要显示这个部件
                if (widget.Area == AreaLocation.BarArea)
                {
                    if (barSwitcher[widget.LocateAreaIndex - 1] == false) continue;   // 如果对应区域的边栏开关为 false，不显示此部件
                }

                // 越界提示消息
                if (widget.Area == AreaLocation.CentralArea)
                {
                    switch (widget.WidgetTypeName)
                    {
                        case "ReporterHLineWidget":
                        case "ReporterVLineWidget":
                            {
                                if ((widget.RowIndex + widget.RowsCount - 1 > page.PageRowsCount) || widget.RowIndex < 0)  // 横线可以在 0 行
                                {
                                    warningMsg.AppendLine($"第 {pageIndex + 1} 页第 {widgetIndex + 1} 个 {widget.WidgetTypeName} 类型的部件行越界。");
                                }

                                if ((widget.ColumnIndex + widget.ColumnsCount - 1 > page.PageColumnsCount) || widget.ColumnIndex < 0)  // 纵线可以在 0 列
                                {
                                    warningMsg.AppendLine($"第 {pageIndex + 1} 页第 {widgetIndex + 1} 个 {widget.WidgetTypeName} 类型的部件列越界。");
                                }

                                break;
                            }
                        default:
                            {
                                if ((widget.RowIndex + widget.RowsCount - 1 > page.PageRowsCount) || widget.RowIndex < 1)  // 其它部件不可在 0 行或 0 列。
                                {
                                    warningMsg.AppendLine($"第 {pageIndex + 1} 页第 {widgetIndex + 1} 个 {widget.WidgetTypeName} 类型的部件行越界。");
                                }

                                if ((widget.ColumnIndex + widget.ColumnsCount - 1 > page.PageColumnsCount) || widget.ColumnIndex < 1)
                                {
                                    warningMsg.AppendLine($"第 {pageIndex + 1} 页第 {widgetIndex + 1} 个 {widget.WidgetTypeName} 类型的部件列越界。");
                                }

                                break;
                            }
                    }
                }

                switch (widget.WidgetTypeName)
                {
                    case "ReporterTextWdiget":
                        {
                            var tw = widget as ReporterTextWdiget;
                            if (tw == null) break;

                            var foreground = BrushManager.GetBrush(tw.Foreground);
                            if (foreground == null)
                                foreground = defForeground;

                            var background = BrushManager.GetBrush(tw.Background);
                            if (background == null)
                                background = defBackground;

                            var wrap = tw.IsAutoWrap.HasValue ? tw.IsAutoWrap.Value : rd.DefaultAutoWrap;

                            var fontName = tw.FontName ?? rd.DefaultFontName;
                            if (fontName == null)
                            {
                                fontName = "Simsun";
                            }

                            var defFontSize = ConvertPtToDiu(rd.DefaultFontSize);
                            if (defFontSize.HasValue == false)
                            {
                                defFontSize = 16;
                            }

                            var fontSize = ConvertPtToDiu(tw.FontSize);
                            if (fontSize.HasValue == false)
                            {
                                fontSize = defFontSize;
                            }

                            var borderWidth = tw.BorderWidth.HasValue ? tw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(tw.BorderColor) ? rd.DefaultBorderColor : tw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Background = background,
                                BorderBrush = borderColor,
                                Padding = new Thickness(0),
                                Margin = new Thickness(0),
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var fontSizeMark = tw.FontSize;
                            var fontWeight = tw.IsBold.HasValue ? tw.IsBold.Value : rd.DefaultIsBold;
                            var fontStyle = tw.IsItalic.HasValue ? tw.IsItalic.Value : rd.DefaultIsItalic;
                            var isUnderLine = tw.IsUnderline.HasValue ? tw.IsUnderline.Value : rd.DefaultIsUnderline;
                            var isStrikeThrough = tw.IsStrikeThrough.HasValue ? tw.IsStrikeThrough.Value : rd.DefaultIsStrikeThrough;
                            var alignment = tw.Alignment.HasValue ? tw.Alignment.Value : rd.DefaultAlignment;

                            string text;

                            if (GetPageNumberText(tw.Text, rowsCount, groupIndex, rd.Pages.Count, pageNumber, out string result))   // 填充页码
                            {
                                text = result;
                            }
                            else
                            {
                                text = tw.Text.Replace("<br>", "\r\n").Replace("&#13;&#10;", "\r\n").Replace("<LineBreak/>", "\r\n");

                                text = Regex.Replace(text, @"(?<=[^\\])\\[S]", "　", RegexOptions.None);
                                text = Regex.Replace(text, @"(?<=[^\\])\\[s]", " ", RegexOptions.None);
                            }

                            var verticalAlignment = tw.VerticalAlignment.HasValue ? tw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var tb = new TextBlock()
                            {
                                Text = text,
                                HorizontalAlignment = HorizontalAlignment.Stretch,
                                VerticalAlignment = verticalAlignment,
                                TextWrapping = wrap ? TextWrapping.Wrap : TextWrapping.NoWrap,
                                Foreground = foreground,
                                FontFamily = new System.Windows.Media.FontFamily(fontName),
                                FontSize = fontSize.Value,
                                FontWeight = fontWeight ? FontWeights.Bold : FontWeights.Normal,
                                FontStyle = fontStyle ? FontStyles.Italic : FontStyles.Normal,
                                TextAlignment = alignment,
                                SnapsToDevicePixels = true,
                            };

                            var underLine = isUnderLine ? TextDecorations.Underline : null;
                            if (underLine != null)
                            {
                                tb.TextDecorations.Add(underLine);
                            }

                            var strikeThrough = isStrikeThrough ? TextDecorations.Strikethrough : null;
                            if (strikeThrough != null)
                            {
                                tb.TextDecorations.Add(strikeThrough);
                            }

                            outborder.Child = tb;

                            mainCanvas.Children.Add(outborder);

                            switch (tw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = tw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = tw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾列不需要加辅助线宽

                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);       // 内容部件在除斜线部件外的最上层
                                        var left = page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (tw.ColumnIndex - 1) + rd.AssistLinesWidth;
                                        var top = page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (tw.RowIndex - 1) + rd.AssistLinesWidth;

                                        Canvas.SetLeft(outborder, left);
                                        Canvas.SetTop(outborder, top);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(tw.LocateAreaIndex, tw.LocateAreasCount > 1);
                                        outborder.Height = area.Height;
                                        outborder.Width = area.Width;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }
                            break;
                        }
                    case "ReporterTextFieldWdiget":
                        {
                            var fw = widget as ReporterTextFieldWdiget;
                            if (fw == null) break;

                            var foreground = BrushManager.GetBrush(fw.Foreground);
                            if (foreground == null)
                                foreground = defForeground;

                            var background = BrushManager.GetBrush(fw.Background);
                            if (background == null)
                                background = defBackground;

                            var wrap = fw.IsAutoWrap.HasValue ? fw.IsAutoWrap.Value : rd.FieldDefaultAutoWrap;

                            var fontName = fw.FontName ?? rd.FieldDefaultFontName;
                            if (fontName == null)
                            {
                                fontName = "Simsun";
                            }

                            var defFontSize = ConvertPtToDiu(rd.DefaultFontSize);
                            if (defFontSize.HasValue == false)
                            {
                                defFontSize = 16;
                            }

                            var fontSize = ConvertPtToDiu(fw.FontSize);
                            if (fontSize.HasValue == false)
                            {
                                fontSize = defFontSize;
                            }

                            var borderWidth = fw.BorderWidth.HasValue ? fw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(fw.BorderColor) ? rd.DefaultBorderColor : fw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Background = background,
                                BorderBrush = borderColor,
                                Padding = new Thickness(0),
                                Margin = new Thickness(0),
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var fontWeight = fw.IsBold.HasValue ? fw.IsBold.Value : rd.FieldDefaultIsBold;
                            var fontStyle = fw.IsItalic.HasValue ? fw.IsItalic.Value : rd.FieldDefaultIsItalic;
                            var isUnderLine = fw.IsUnderline.HasValue ? fw.IsUnderline.Value : rd.FieldDefaultIsUnderline;
                            var isStrikeThrough = fw.IsStrikeThrough.HasValue ? fw.IsStrikeThrough.Value : rd.FieldDefaultIsStrikeThrough;
                            var alignment = fw.Alignment.HasValue ? fw.Alignment.Value : rd.FieldDefaultAlignment;

                            var verticalAlignment = fw.VerticalAlignment.HasValue ? fw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var tb = new FieldTextBlock()
                            {
                                Text = "{" + fw.Text.Replace("<br>", "\r\n").Replace("&#13;&#10;", "\r\n").Replace("<LineBreak/>", "\r\n").Trim(new char[] { '{', '｛', '}', '｝' }) + "}",
                                FieldName = fw.Text,
                                HorizontalAlignment = HorizontalAlignment.Stretch,
                                VerticalAlignment = verticalAlignment,
                                TextWrapping = wrap ? TextWrapping.Wrap : TextWrapping.NoWrap,
                                Foreground = foreground,
                                FontFamily = new System.Windows.Media.FontFamily(fontName),
                                FontSize = fontSize.Value,
                                FontWeight = fontWeight ? FontWeights.Bold : FontWeights.Normal,
                                FontStyle = fontStyle ? FontStyles.Italic : FontStyles.Normal,
                                TextAlignment = alignment,
                                SnapsToDevicePixels = true,
                                ForeColor = foreground,   // 字段有效时用此值，无效时用“错误信息前景色”。
                            };

                            var underLine = isUnderLine ? TextDecorations.Underline : null;
                            if (underLine != null)
                            {
                                tb.TextDecorations.Add(underLine);
                            }

                            var strikeThrough = isStrikeThrough ? TextDecorations.Strikethrough : null;
                            if (strikeThrough != null)
                            {
                                tb.TextDecorations.Add(strikeThrough);
                            }

                            outborder.Child = tb;

                            mainCanvas.Children.Add(outborder);
                            switch (fw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = fw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = fw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾列不需要加辅助线宽
                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);       // 内容部件在除斜线部件外的最上层

                                        Canvas.SetLeft(outborder, page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (fw.ColumnIndex - 1) + rd.AssistLinesWidth);
                                        Canvas.SetTop(outborder, page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (fw.RowIndex - 1) + rd.AssistLinesWidth);

                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(fw.LocateAreaIndex, fw.LocateAreasCount > 1);
                                        outborder.Height = area.Height;
                                        outborder.Width = area.Width;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }

                            fieldElements.Add(tb); // 这样就可以填充了（无论是在边栏还是版芯都应支持填充）
                            break;
                        }
                    case "ReporterCharsRowWdiget":
                        {
                            var cw = widget as ReporterCharsRowWdiget;
                            if (cw == null) break;

                            if (cw.IsIdNumberWidget == false && string.IsNullOrEmpty(cw.FillChars) == false && cw.FillChars != "^")
                            {
                                questionWidgetFillChars.Append(cw.FillChars + "|");  // 支持多项选择
                            }

                            var foreground = BrushManager.GetBrush(cw.Foreground);
                            if (foreground == null)
                                foreground = defForeground;

                            var background = BrushManager.GetBrush(cw.Background);
                            if (background == null)
                                background = defBackground;

                            var wrap = cw.IsAutoWrap.HasValue ? cw.IsAutoWrap.Value : rd.FieldDefaultAutoWrap;

                            var fontName = cw.FontName ?? rd.FieldDefaultFontName;
                            if (fontName == null)
                            {
                                fontName = "Simsun";
                            }

                            var defFontSize = ConvertPtToDiu(rd.FieldDefaultFontSize.ToString());
                            if (defFontSize.HasValue == false)
                            {
                                defFontSize = 16;
                            }

                            var fontSize = ConvertPtToDiu(cw.FontSize);
                            if (fontSize.HasValue == false)
                            {
                                fontSize = defFontSize;
                            }

                            var borderWidth = cw.BorderWidth.HasValue ? cw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(cw.BorderColor) ? rd.DefaultBorderColor : cw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var fontSizeMark = cw.FontSize;
                            var fontWeight = cw.IsBold.HasValue ? cw.IsBold.Value : rd.FieldDefaultIsBold;
                            var fontStyle = cw.IsItalic.HasValue ? cw.IsItalic.Value : rd.FieldDefaultIsItalic;

                            var isUnderLine = cw.IsUnderline.HasValue ? cw.IsUnderline.Value : rd.FieldDefaultIsUnderline;
                            var isStrikeThrough = cw.IsStrikeThrough.HasValue ? cw.IsStrikeThrough.Value : rd.FieldDefaultIsStrikeThrough;
                            var alignment = cw.Alignment.HasValue ? cw.Alignment.Value : rd.FieldDefaultAlignment;

                            var underLine = isUnderLine ? TextDecorations.Underline : null;
                            var strikeThrough = isStrikeThrough ? TextDecorations.Strikethrough : null;

                            var textSpans = new List<QuestionOptionEntry>();
                            if (string.IsNullOrEmpty(cw.NumberText) == false)
                            {
                                // 加题号列
                                textSpans.Add(new QuestionOptionEntry() { IsQuestionNumber = true, Text = cw.NumberText });
                            }

                            foreach (var c in cw.Chars)
                            {
                                textSpans.Add(new QuestionOptionEntry() { IsQuestionNumber = false, Text = c.ToString() });
                            }

                            if (cw.IsIdNumberWidget)
                            {
                                idNumber++;
                                idWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）编号>{idNumber}：");
                            }
                            else
                            {
                                questionsNumber++;

                                if (string.IsNullOrEmpty(cw.NumberText) || cw.NumberText == "^")
                                {
                                    questionWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）试题>" + questionsNumber.ToString() + "：");
                                }
                                else
                                {
                                    questionWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）试题>" + cw.NumberText + "：");  // 优先使用
                                }
                            }

                            for (int i = 0; i < textSpans.Count; i++)
                            {
                                var option = textSpans[i];

                                var outborder = new Border()
                                {
                                    Background = System.Windows.Media.Brushes.Transparent,
                                    BorderThickness = new Thickness(0),
                                };

                                outborder.Height = page.CellHeight;
                                outborder.Width = page.CellWidth;

#if DEBUG
                                outborder.Background = System.Windows.Media.Brushes.AliceBlue;
#endif

                                var innerborder = new System.Windows.Controls.Border()
                                {
                                    Background = background,
                                    Padding = new Thickness(0),
                                    Margin = new Thickness(0),
                                    BorderThickness = new Thickness(borderWidth),
                                    SnapsToDevicePixels = true,
                                    // Width = outborder.Width / 2,  // 题号可能是两位数甚至更宽，所以要允许宽一点
                                    Height = outborder.Height / 2,
                                    HorizontalAlignment = HorizontalAlignment.Center,
                                    VerticalAlignment = VerticalAlignment.Center,
                                };

                                if (option.IsQuestionNumber)
                                {
                                    innerborder.Width = outborder.Width * 0.8;   // 这个方案并不彻底，但在可能的范围内也算是尽量留了空间了。
                                    innerborder.BorderBrush = System.Windows.Media.Brushes.Transparent;
                                }
                                else
                                {
                                    innerborder.Width = outborder.Width / 2;   // 这个方案并不彻底，但在可能的范围内也算是尽量留了空间了。
                                    innerborder.BorderBrush = borderColor;   // 渐变实现开口虽然好看，但未填涂时无法识别。
                                }

                                #region 废弃代码，答题卡识读程序无法识别未填涂的开口选项
                                //var solidBrush = borderColor as System.Windows.Media.SolidColorBrush;

                                //if (solidBrush == null)
                                //{
                                //    innerborder.BorderBrush = borderColor;
                                //}
                                //else
                                //{
                                //    if (option.IsQuestionNumber)
                                //    {
                                //        innerborder.BorderBrush = System.Windows.Media.Brushes.Transparent;
                                //    }
                                //    else
                                //    {
                                //        var color = solidBrush.Color;
                                //        var borderBrush = new LinearGradientBrush();
                                //        borderBrush.StartPoint = new System.Windows.Point(0, 0);
                                //        borderBrush.EndPoint = new System.Windows.Point(1, 0);
                                //        borderBrush.GradientStops.Add(new GradientStop(color, 0));
                                //        borderBrush.GradientStops.Add(new GradientStop(color, 0.25));
                                //        borderBrush.GradientStops.Add(new GradientStop(System.Windows.Media.Brushes.Transparent.Color, 0.25));
                                //        borderBrush.GradientStops.Add(new GradientStop(System.Windows.Media.Brushes.Transparent.Color, 0.75));
                                //        borderBrush.GradientStops.Add(new GradientStop(color, 0.75));
                                //        borderBrush.GradientStops.Add(new GradientStop(color, 1));
                                //        innerborder.BorderBrush = borderBrush;
                                //    }
                                //}
                                #endregion

                                var tb = new TextBlock()
                                {
                                    Foreground = foreground,
                                    FontFamily = string.IsNullOrWhiteSpace(fontName) ? null : new System.Windows.Media.FontFamily(fontName),
                                    FontSize = fontSize.Value,
                                    FontWeight = fontWeight ? FontWeights.Bold : FontWeights.Normal,
                                    FontStyle = fontStyle ? FontStyles.Italic : FontStyles.Normal,
                                    TextAlignment = alignment,
                                    HorizontalAlignment = HorizontalAlignment.Stretch,
                                    VerticalAlignment = VerticalAlignment.Center,
                                };

                                if (i == 0 && option.IsQuestionNumber && option.Text == "^")
                                {
                                    tb.Text = questionsNumber.ToString();
                                }
                                else tb.Text = option.Text;

                                if (rd.FillCharsRowOrColumn && cw.FillChars != null && cw.FillChars.Contains(tb.Text))
                                {
                                    innerborder.Background = foreground;
                                }

                                if (underLine != null)
                                {
                                    tb.TextDecorations.Add(underLine);
                                }

                                if (strikeThrough != null)
                                {
                                    tb.TextDecorations.Add(strikeThrough);
                                }

                                innerborder.Child = tb;
                                outborder.Child = innerborder;
                                mainCanvas.Children.Add(outborder);

                                Canvas.SetZIndex(outborder, ContentWidgetsZIndex);       // 内容部件在除斜线部件外的最上层
                                var left = page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (cw.ColumnIndex - 1) + rd.AssistLinesWidth + i * (page.CellWidth + rd.AssistLinesWidth);
                                var top = page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (cw.RowIndex - 1) + rd.AssistLinesWidth;  // 行，纵坐标不变

                                Canvas.SetLeft(outborder, left);
                                Canvas.SetTop(outborder, top);

                                System.Windows.Point pointLt;
                                System.Windows.Point pointRb;

                                if (option.IsQuestionNumber)
                                {
                                    pointLt = new System.Windows.Point(left - page.LeftTopPoint.X + outborder.Width / 10,
                                                                       top - page.LeftTopPoint.Y + outborder.Height / 4);
                                }
                                else
                                {
                                    pointLt = new System.Windows.Point(left - page.LeftTopPoint.X + outborder.Width / 4,
                                                                       top - page.LeftTopPoint.Y + outborder.Height / 4);
                                }
                                
                                pointRb = new System.Windows.Point(pointLt.X + innerborder.Width, pointLt.Y + innerborder.Height);

                                if (cw.IsIdNumberWidget)
                                {
                                    idWidgetAreas.Append(Utils.Text.FatAlphaAndNumbersToThin(option.Text + ":"));
                                    idWidgetAreas.Append($"{cw.RowIndex},{cw.ColumnIndex + i};");
                                }
                                else
                                {
                                    if (option.IsQuestionNumber == false)  // 题号不需要输出
                                    {
                                        questionWidgetAreas.Append(Utils.Text.FatAlphaAndNumbersToThin(option.Text + ":"));
                                        if (cw.FillChars != null && cw.FillChars.Contains(tb.Text))
                                        {
                                            // 说明此区域应填涂为答案
                                            questionWidgetAreas.Append("*");     // 应加分
                                        }
                                        else
                                        {
                                            questionWidgetAreas.Append("~");     // 应减分
                                        }
                                        questionWidgetAreas.Append($"{cw.RowIndex},{cw.ColumnIndex + i};");
                                    }
                                }

#if DEBUG
                                var cover = new System.Windows.Shapes.Rectangle()
                                {
                                    Fill = System.Windows.Media.Brushes.Red,
                                    Width = pointRb.X - pointLt.X,
                                    Height = pointRb.Y - pointLt.Y,
                                };
                                mainCanvas.Children.Add(cover);
                                Canvas.SetLeft(cover, pointLt.X + page.LeftTopPoint.X);
                                Canvas.SetTop(cover, pointLt.Y + page.LeftTopPoint.Y);
                                Canvas.SetZIndex(cover, ContentWidgetsZIndex);
#endif
                            }

                            if (cw.IsIdNumberWidget)
                            {
                                idWidgetAreas.Append("\r\n");
                            }
                            else
                            {
                                questionWidgetAreas.Append("\r\n");
                            }

                            break;
                        }
                    case "ReporterCharsColumnWdiget":
                        {
                            var cw = widget as ReporterCharsColumnWdiget;
                            if (cw == null) break;

                            if (cw.IsIdNumberWidget == false && string.IsNullOrEmpty(cw.FillChars) == false && cw.FillChars != "^")
                            {
                                questionWidgetFillChars.Append(cw.FillChars + "|");  // 支持多项选择
                            }

                            var foreground = BrushManager.GetBrush(cw.Foreground);
                            if (foreground == null)
                                foreground = defForeground;

                            var background = BrushManager.GetBrush(cw.Background);
                            if (background == null)
                                background = defBackground;

                            var wrap = cw.IsAutoWrap.HasValue ? cw.IsAutoWrap.Value : rd.FieldDefaultAutoWrap;

                            var fontName = cw.FontName ?? rd.FieldDefaultFontName;
                            if (fontName == null)
                            {
                                fontName = "Simsun";
                            }

                            var defFontSize = ConvertPtToDiu(rd.FieldDefaultFontSize.ToString());
                            if (defFontSize.HasValue == false)
                            {
                                defFontSize = 16;
                            }

                            var fontSize = ConvertPtToDiu(cw.FontSize);
                            if (fontSize.HasValue == false)
                            {
                                fontSize = defFontSize;
                            }

                            var borderWidth = cw.BorderWidth.HasValue ? cw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(cw.BorderColor) ? rd.DefaultBorderColor : cw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var fontSizeMark = cw.FontSize;
                            var fontWeight = cw.IsBold.HasValue ? cw.IsBold.Value : rd.FieldDefaultIsBold;
                            var fontStyle = cw.IsItalic.HasValue ? cw.IsItalic.Value : rd.FieldDefaultIsItalic;

                            var isUnderLine = cw.IsUnderline.HasValue ? cw.IsUnderline.Value : rd.FieldDefaultIsUnderline;
                            var isStrikeThrough = cw.IsStrikeThrough.HasValue ? cw.IsStrikeThrough.Value : rd.FieldDefaultIsStrikeThrough;
                            var alignment = cw.Alignment.HasValue ? cw.Alignment.Value : rd.FieldDefaultAlignment;

                            var underLine = isUnderLine ? TextDecorations.Underline : null;
                            var strikeThrough = isStrikeThrough ? TextDecorations.Strikethrough : null;

                            var textSpans = new List<QuestionOptionEntry>();
                            if (string.IsNullOrEmpty(cw.NumberText) == false)
                            {
                                // 加题号行
                                textSpans.Add(new QuestionOptionEntry() { IsQuestionNumber = true, Text = cw.NumberText });
                            }

                            foreach (var c in cw.Chars)
                            {
                                textSpans.Add(new QuestionOptionEntry() { IsQuestionNumber = false, Text = c.ToString() });
                            }

                            if (cw.IsIdNumberWidget)
                            {
                                idNumber++;
                                idWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）编号>{idNumber}：");
                            }
                            else
                            {
                                questionsNumber++;

                                if (string.IsNullOrEmpty(cw.NumberText) || cw.NumberText == "^")
                                {
                                    questionWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）试题>" + questionsNumber.ToString() + "：");
                                }
                                else
                                {
                                    questionWidgetAreas.Append($"（{page.ActualContentWidth}，{page.ActualContentHeight}）试题>" + cw.NumberText + "：");  // 优先使用
                                }
                            }

                            for (int i = 0; i < textSpans.Count; i++)
                            {
                                var option = textSpans[i];

                                var outborder = new Border()
                                {
                                    Background = System.Windows.Media.Brushes.Transparent,
                                    BorderThickness = new Thickness(0),
                                };

                                outborder.Height = page.CellHeight;
                                outborder.Width = page.CellWidth;

#if DEBUG
                                outborder.Background = System.Windows.Media.Brushes.AliceBlue;
#endif

                                var innerborder = new System.Windows.Controls.Border()
                                {
                                    Background = background,
                                    Padding = new Thickness(0),
                                    Margin = new Thickness(0),
                                    BorderThickness = new Thickness(borderWidth),
                                    SnapsToDevicePixels = true,
                                    // Width = outborder.Width / 2,  // 字符列的题号可能有两位或更宽，宽度应特别处理
                                    Height = outborder.Height / 2,
                                    HorizontalAlignment = HorizontalAlignment.Center,
                                    VerticalAlignment = VerticalAlignment.Center,
                                };

                                if (option.IsQuestionNumber)
                                {
                                    innerborder.Width = outborder.Width * 0.8;   // 这个方案并不彻底，但在可能的范围内也算是尽量留了空间了。
                                    innerborder.BorderBrush = System.Windows.Media.Brushes.Transparent;
                                }
                                else
                                {
                                    innerborder.Width = outborder.Width / 2;
                                    innerborder.BorderBrush = borderColor;   // 渐变实现开口虽然好看，但未填涂时无法识别。
                                }

                                var tb = new TextBlock()
                                {
                                    Foreground = foreground,
                                    FontFamily = string.IsNullOrWhiteSpace(fontName) ? null : new System.Windows.Media.FontFamily(fontName),
                                    FontSize = fontSize.Value,
                                    FontWeight = fontWeight ? FontWeights.Bold : FontWeights.Normal,
                                    FontStyle = fontStyle ? FontStyles.Italic : FontStyles.Normal,
                                    TextAlignment = alignment,
                                    HorizontalAlignment = HorizontalAlignment.Stretch,
                                    VerticalAlignment = VerticalAlignment.Center,
                                };

                                if (i == 0 && option.IsQuestionNumber && option.Text == "^")
                                {
                                    tb.Text = questionsNumber.ToString();
                                }
                                else tb.Text = option.Text;

                                if (rd.FillCharsRowOrColumn && cw.FillChars != null && cw.FillChars.Contains(tb.Text))
                                {
                                    innerborder.Background = foreground;
                                }

                                if (underLine != null)
                                {
                                    tb.TextDecorations.Add(underLine);
                                }

                                if (strikeThrough != null)
                                {
                                    tb.TextDecorations.Add(strikeThrough);
                                }

                                innerborder.Child = tb;
                                outborder.Child = innerborder;
                                mainCanvas.Children.Add(outborder);

                                Canvas.SetZIndex(outborder, ContentWidgetsZIndex);       // 内容部件在除斜线部件外的最上层
                                var left = page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (cw.ColumnIndex - 1) + rd.AssistLinesWidth;   // 列，横坐标不变
                                var top = page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (cw.RowIndex - 1) + rd.AssistLinesWidth + i * (page.CellHeight + rd.AssistLinesWidth);

                                Canvas.SetLeft(outborder, left);
                                Canvas.SetTop(outborder, top);

                                System.Windows.Point pointLt;
                                System.Windows.Point pointRb;

                                if (option.IsQuestionNumber)
                                {
                                    pointLt = new System.Windows.Point(left - page.LeftTopPoint.X + outborder.Width / 10,
                                                                       top - page.LeftTopPoint.Y + outborder.Height / 4);
                                }
                                else
                                {
                                    pointLt = new System.Windows.Point(left - page.LeftTopPoint.X + outborder.Width / 4,
                                                                       top - page.LeftTopPoint.Y + outborder.Height / 4);
                                }
                                
                                pointRb = new System.Windows.Point(pointLt.X + innerborder.Width, pointLt.Y + innerborder.Height);
                                
                                if (cw.IsIdNumberWidget)
                                {
                                    idWidgetAreas.Append(Utils.Text.FatAlphaAndNumbersToThin(option.Text + ":"));
                                    idWidgetAreas.Append($"{cw.RowIndex + i},{cw.ColumnIndex};");
                                }
                                else
                                {
                                    if (option.IsQuestionNumber == false)   // 题号不需要输出
                                    {
                                        questionWidgetAreas.Append(Utils.Text.FatAlphaAndNumbersToThin(option.Text + ":"));         // 题支需要这个

                                        if (cw.FillChars != null && cw.FillChars.Contains(tb.Text))
                                        {
                                            // 说明此区域应填涂为答案
                                            questionWidgetAreas.Append("*");     // 应加分
                                        }
                                        else
                                        {
                                            questionWidgetAreas.Append("~");     // 应减分
                                        }
                                        questionWidgetAreas.Append($"{cw.RowIndex + i},{cw.ColumnIndex};");
                                    }
                                }

#if DEBUG
                                var cover = new System.Windows.Shapes.Rectangle()
                                {
                                    Fill = System.Windows.Media.Brushes.Red,
                                    Width = pointRb.X - pointLt.X,
                                    Height = pointRb.Y - pointLt.Y,
                                };
                                mainCanvas.Children.Add(cover);
                                Canvas.SetLeft(cover, pointLt.X + page.LeftTopPoint.X);
                                Canvas.SetTop(cover, pointLt.Y + page.LeftTopPoint.Y);
                                Canvas.SetZIndex(cover, ContentWidgetsZIndex);
#endif
                            }

                            if (cw.IsIdNumberWidget)
                            {
                                idWidgetAreas.Append("\r\n");
                            }
                            else
                            {
                                questionWidgetAreas.Append("\r\n");
                            }

                            break;
                        }
                    case "ReporterHLineWidget":
                        {
                            var hw = widget as ReporterHLineWidget;
                            if (hw == null) break;

                            var height = hw.LineWidth.HasValue ? hw.LineWidth.Value : rd.DefaultLineWidth;
                            if (height == 0) break;  // 线条宽度为零时，不需要绘制。

                            var strokeBrush = BrushManager.GetBrush(hw.LineColor);
                            if (strokeBrush == null)
                                strokeBrush = defLineColor;

                            var strokeThickness = hw.LineWidth.HasValue ? hw.LineWidth.Value : rd.DefaultLineWidth;

                            var dashStyle = GetDashStyle(hw.LineDashType);
                            if (dashStyle == null)
                            {
                                dashStyle = defLineDashType;
                            }

                            var y = page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * hw.RowIndex;
                            y += (int)(strokeThickness / 2);
                            var x1 = page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (hw.ColumnIndex - 1) + rd.AssistLinesWidth;
                            // var x1 = page.LeftTopPoint.X + (hw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth);
                            var x2 = page.LeftTopPoint.X + (hw.ColumnIndex - 1 + hw.ColumnsCount) * (page.CellWidth + rd.AssistLinesWidth);

                            var overThisRow = false;
                            if (height < 0)
                            {
                                height = page.CellHeight;   // 小于 0 时，视为与所在行同高（且定位时覆盖当前行）
                                strokeThickness = height;
                                overThisRow = true;
                            }

                            var hLine = new System.Windows.Shapes.Line()
                            {
                                // Height = height,
                                // Width = x2 - x1 - rd.AssistLinesWidth,
                                X1 = x1,
                                X2 = x2,
                                Stroke = strokeBrush,
                                StrokeThickness = strokeThickness,
                                StrokeDashArray = dashStyle,
                                StrokeEndLineCap = PenLineCap.Flat,
                                StrokeStartLineCap = PenLineCap.Flat,
                                StrokeLineJoin = PenLineJoin.Miter,
                                StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                                SnapsToDevicePixels = true,
                            };

                            mainCanvas.Children.Add(hLine);

                            switch (hw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        hLine.X1 = x1;
                                        hLine.X2 = x2;

                                        if (overThisRow)
                                        {
                                            if (page.CellHeight % 2 == 1)
                                            {
                                                hLine.Y2 = hLine.Y1 = y - (int)(page.CellHeight / 2) + 0.5;
                                            }
                                            else
                                            {
                                                hLine.Y2 = hLine.Y1 = y - (int)(page.CellHeight / 2);
                                            }
                                            hLine.StrokeDashArray = GetDashStyle("实线");  // 这个总是实线
                                        }
                                        else
                                        {
                                            if (strokeThickness % 2 == 1)
                                            {
                                                hLine.Y2 = hLine.Y1 = y + 0.5;
                                            }
                                            else
                                            {
                                                hLine.Y2 = hLine.Y1 = y;
                                            }
                                        }
                                        Canvas.SetZIndex(hLine, AdornerWidgetsZIndex);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(hw.LocateAreaIndex, hw.LocateAreasCount > 1);
                                        hLine.X1 = area.Left;
                                        hLine.X2 = area.Right;
                                        hLine.Y1 = area.Bottom;
                                        hLine.Y2 = area.Bottom;
                                        break;
                                    }
                            }
                            break;
                        }
                    case "ReporterVLineWidget":
                        {
                            var vw = widget as ReporterVLineWidget;
                            if (vw == null) break;

                            var width = vw.LineWidth.HasValue ? vw.LineWidth.Value : rd.DefaultLineWidth;
                            if (width == 0) break;  // 线宽为 0 时，不需要绘制

                            var strokeBrush = BrushManager.GetBrush(vw.LineColor);
                            if (strokeBrush == null)
                                strokeBrush = defLineColor;

                            var strokeThickness = vw.LineWidth.HasValue ? vw.LineWidth.Value : rd.DefaultLineWidth;

                            var dashStyle = GetDashStyle(vw.LineDashType);
                            if (dashStyle == null)
                            {
                                dashStyle = defLineDashType;
                            }

                            var x = page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * vw.ColumnIndex;
                            var y1 = page.LeftTopPoint.Y + (vw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth) + rd.AssistLinesWidth;
                            var y2 = page.LeftTopPoint.Y + (vw.RowIndex - 1 + vw.RowsCount) * (page.CellHeight + rd.AssistLinesWidth);

                            var overThisColumn = false;
                            if (width < 0)
                            {
                                width = page.CellWidth;   // 小于 0 时，视为与所在列同宽（且定位时覆盖当前列）
                                strokeThickness = width;
                                overThisColumn = true;
                            }

                            var vLine = new System.Windows.Shapes.Line()
                            {
                                Stroke = strokeBrush,
                                StrokeThickness = strokeThickness,
                                StrokeDashArray = dashStyle,
                                StrokeEndLineCap = PenLineCap.Flat,
                                StrokeStartLineCap = PenLineCap.Flat,
                                StrokeLineJoin = PenLineJoin.Miter,
                                StrokeDashCap = PenLineCap.Square,             // 会影响点、划线(应为Square)
                                SnapsToDevicePixels = true,
                            };

                            mainCanvas.Children.Add(vLine);

                            switch (vw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        vLine.Y1 = y1;
                                        vLine.Y2 = y2;
                                        if (overThisColumn)
                                        {
                                            //Canvas.SetLeft(vLine, x - page.CellWidth);
                                            if (page.CellWidth % 2 == 1)
                                            {
                                                vLine.X2 = vLine.X1 = x - (int)(page.CellWidth / 2) - 0.5;
                                            }
                                            else
                                            {
                                                vLine.X2 = vLine.X1 = x - (int)(page.CellWidth / 2);
                                            }
                                            vLine.StrokeDashArray = GetDashStyle("实线");  // 这个总是实线
                                        }
                                        else
                                        {
                                            if (strokeThickness % 2 == 1)
                                            {
                                                vLine.X2 = vLine.X1 = x + (int)(strokeThickness / 2) + 0.5;
                                            }
                                            else
                                            {
                                                vLine.X2 = vLine.X1 = x + (int)(strokeThickness / 2);
                                            }
                                        }
                                        Canvas.SetZIndex(vLine, AdornerWidgetsZIndex);

                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(vw.LocateAreaIndex, vw.LocateAreasCount > 1);
                                        vLine.X1 = area.Right;
                                        vLine.X2 = area.Right;
                                        vLine.Y1 = area.Top;
                                        vLine.Y2 = area.Bottom;
                                        break;
                                    }
                            }
                            break;
                        }
                    case "ReporterBoxWidget":
                        {
                            var bw = widget as ReporterBoxWidget;
                            if (bw == null) break;

                            var zIndex = bw.BackgroundLayer ? BackgroundGridZIndex : ForegroundGridZIndex;

                            // 这里绘制内部线条
                            var lineStrokeBrush = BrushManager.GetBrush(bw.LineColor);
                            if (lineStrokeBrush == null)
                                lineStrokeBrush = defLineColor;

                            var strokeThickness = bw.LineWidth.HasValue ? bw.LineWidth.Value : rd.DefaultLineWidth;

                            var lineDashStyle = GetDashStyle(bw.LineDashType);
                            if (lineDashStyle == null)
                            {
                                lineDashStyle = defLineDashType;
                            }

                            if (bw.BackgroundLayer && bw.Area != AreaLocation.BarArea)         // 在底层时，只绘制网格线，不绘制外框；在边栏时，不绘制网格线。
                            {
                                // 用循环解决
                                // 竖线
                                if (bw.BaseColumnsCount > 0)
                                {
                                    for (int i = 1; i < bw.ColumnsCount; i++)  // 尾列不画，等待边框
                                    {
                                        if (i % bw.BaseColumnsCount == 0)
                                        {
                                            var x = page.LeftTopPoint.X + (bw.ColumnIndex + i - 1) * (page.CellWidth + rd.AssistLinesWidth);
                                            var y1 = page.LeftTopPoint.Y + (bw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth) + rd.AssistLinesWidth;
                                            var y2 = page.LeftTopPoint.Y + (bw.RowIndex - 1 + bw.RowsCount) * (page.CellHeight + rd.AssistLinesWidth);

                                            var width = bw.LineWidth.HasValue ? bw.LineWidth.Value : rd.DefaultLineWidth;
                                            if (width < 1) width = 1;  // 网格线宽度至少为 1

                                            var vLine = new System.Windows.Shapes.Line()
                                            {
                                                //Width = width,
                                                //Height = y2 - y1,
                                                Y1 = y1,
                                                Y2 = y2,
                                                Stroke = lineStrokeBrush,
                                                StrokeThickness = strokeThickness,
                                                StrokeDashArray = lineDashStyle,
                                                SnapsToDevicePixels = true,
                                                StrokeEndLineCap = PenLineCap.Flat,
                                                StrokeStartLineCap = PenLineCap.Flat,
                                                StrokeLineJoin = PenLineJoin.Miter,
                                                StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                                            };
                                            if (strokeThickness % 2 == 1)
                                            {
                                                vLine.X2 = vLine.X1 = x + (int)(strokeThickness / 2) + 0.5;
                                            }
                                            else
                                            {
                                                vLine.X2 = vLine.X1 = x + (int)(strokeThickness / 2);
                                            }
                                            mainCanvas.Children.Add(vLine);
                                            //Canvas.SetLeft(vLine, x);
                                            //Canvas.SetTop(vLine, y1);
                                            Canvas.SetZIndex(vLine, zIndex);
                                        }
                                    }
                                }

                                // 横线
                                if (bw.BaseRowsCount > 0)
                                {
                                    for (int i = 1; i < bw.RowsCount; i++)  // 尾行不画，等待边框
                                    {
                                        if (i % bw.BaseRowsCount == 0)
                                        {
                                            var y = page.LeftTopPoint.Y + (bw.RowIndex + i - 1) * (page.CellHeight + rd.AssistLinesWidth);
                                            var x1 = page.LeftTopPoint.X + (bw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth) + rd.AssistLinesWidth;
                                            var x2 = page.LeftTopPoint.X + (bw.ColumnIndex - 1 + bw.ColumnsCount) * (page.CellWidth + rd.AssistLinesWidth);

                                            var height = bw.LineWidth.HasValue ? bw.LineWidth.Value : rd.DefaultLineWidth;
                                            if (height < 1) height = 1;    // 网格线宽度至少为 1

                                            var hLine = new System.Windows.Shapes.Line()
                                            {
                                                //Height = height,
                                                //Width = x2 - x1,
                                                X1 = x1,
                                                X2 = x2,
                                                Stroke = lineStrokeBrush,
                                                StrokeThickness = strokeThickness,
                                                StrokeDashArray = lineDashStyle,
                                                SnapsToDevicePixels = true,
                                                StrokeEndLineCap = PenLineCap.Flat,
                                                StrokeStartLineCap = PenLineCap.Flat,
                                                StrokeLineJoin = PenLineJoin.Miter,
                                                StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                                            };
                                            if (strokeThickness % 2 == 1)
                                            {
                                                hLine.Y2 = hLine.Y1 = y + (int)(strokeThickness / 2) + 0.5;
                                            }
                                            else
                                            {
                                                hLine.Y2 = hLine.Y1 = y + (int)(strokeThickness / 2);
                                            }
                                            mainCanvas.Children.Add(hLine);
                                            //Canvas.SetLeft(hLine, x1);
                                            //Canvas.SetTop(hLine, y);
                                            Canvas.SetZIndex(hLine, zIndex);
                                        }
                                    }
                                }
                            }

                            if (bw.BackgroundLayer == false) // 在上层时，只绘制外框，不绘制网格线。
                            {
                                var boxStrokeBrush = BrushManager.GetBrush(bw.BoxLineColor);
                                if (boxStrokeBrush == null)
                                    boxStrokeBrush = defBoxColor;

                                var boxStrokeThickness = bw.BoxLineWidth.HasValue ? bw.BoxLineWidth.Value : rd.DefaultBoxLineWidth;
                                if (boxStrokeThickness == 0) break;   // 宽度为 0 时不绘制

                                var background = BrushManager.GetBrush(bw.Background);
                                if (background == null)
                                    background = defBoxBackground;

                                var width = bw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) + boxStrokeThickness * 2 - rd.AssistLinesWidth;
                                var height = bw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) + boxStrokeThickness * 2 - rd.AssistLinesWidth;

                                var boxDashStyle = GetDashStyle(bw.BoxLineDashType);
                                if (boxDashStyle == null)
                                {
                                    boxDashStyle = defBoxDashType;
                                }

                                var overRegion = false;
                                if (boxStrokeThickness < 0)
                                {
                                    overRegion = true;    // 宽度小于 0 时，自动覆盖整个区域
                                    width = bw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;
                                    height = bw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;
                                }

                                var r = new System.Windows.Shapes.Rectangle()
                                {
                                    Width = width,
                                    Height = height,
                                    Fill = background,
                                    Stroke = boxStrokeBrush,
                                    StrokeThickness = boxStrokeThickness,
                                    StrokeDashArray = boxDashStyle,
                                    StrokeDashCap = PenLineCap.Square,      // 会影响点、划线(应为Square)
                                    StrokeEndLineCap = PenLineCap.Flat,
                                    StrokeStartLineCap = PenLineCap.Flat,
                                    StrokeLineJoin = PenLineJoin.Miter,
                                    SnapsToDevicePixels = true
                                };
                                mainCanvas.Children.Add(r);
                                Canvas.SetZIndex(r, zIndex);


                                answerSheetBoxBorderWidthRate.Append(((double)boxStrokeThickness / width).ToString());
                                answerSheetBoxBorderHeightRate.Append(((double)boxStrokeThickness / height).ToString());


                                switch (bw.Area)
                                {
                                    case AreaLocation.CentralArea:
                                        {
                                            double left, top;
                                            if (overRegion)
                                            {
                                                Canvas.SetLeft(r, left = page.LeftTopPoint.X + (bw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth));
                                                Canvas.SetTop(r, top = page.LeftTopPoint.Y + (bw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth));
                                            }
                                            else
                                            {
                                                Canvas.SetLeft(r, left = page.LeftTopPoint.X + (bw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth) - boxStrokeThickness + rd.AssistLinesWidth);
                                                Canvas.SetTop(r, top = page.LeftTopPoint.Y + (bw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth) - boxStrokeThickness + rd.AssistLinesWidth);
                                            }

                                            // 只有在这里才需要绘制答题卡需要的四角装饰器

                                            if (bw.Adorner)
                                            {
                                                var adornerWidth = Math.Min(page.CellHeight, page.CellWidth);

                                                // 左上角
                                                var ad1 = new Border()
                                                {
                                                    Width = adornerWidth / 2,
                                                    Height = adornerWidth / 2,
                                                    Background = boxStrokeBrush,
                                                };
                                                mainCanvas.Children.Add(ad1);
                                                Canvas.SetZIndex(ad1, zIndex);
                                                Canvas.SetLeft(ad1, left + r.StrokeThickness);
                                                Canvas.SetTop(ad1, top + r.StrokeThickness);

#if DEBUG
                                                ad1.Background = System.Windows.Media.Brushes.Red;
#endif

                                                // 右上角
                                                var ad2 = new Border()
                                                {
                                                    Width = adornerWidth / 2,
                                                    Height = adornerWidth / 2,
                                                    Background = boxStrokeBrush,
                                                };
                                                mainCanvas.Children.Add(ad2);
                                                Canvas.SetZIndex(ad2, zIndex);
                                                Canvas.SetLeft(ad2, left + r.Width - adornerWidth / 2 - r.StrokeThickness);
                                                Canvas.SetTop(ad2, top + r.StrokeThickness);

#if DEBUG
                                                ad2.Background = System.Windows.Media.Brushes.Red;
#endif

                                                // 左下角
                                                var ad3 = new Border()
                                                {
                                                    Width = adornerWidth / 2,
                                                    Height = adornerWidth / 2,
                                                    Background = boxStrokeBrush,
                                                };
                                                mainCanvas.Children.Add(ad3);
                                                Canvas.SetZIndex(ad3, zIndex);
                                                Canvas.SetLeft(ad3, left + r.StrokeThickness);
                                                Canvas.SetTop(ad3, top + r.Height - adornerWidth / 2 - r.StrokeThickness);

#if DEBUG
                                                ad3.Background = System.Windows.Media.Brushes.Red;
#endif

                                                // 右下角

                                                var ad4 = new Border()
                                                {
                                                    Width = adornerWidth / 2,
                                                    Height = adornerWidth / 2,
                                                    Background = System.Windows.Media.Brushes.Transparent,
                                                    BorderBrush = boxStrokeBrush,
                                                    BorderThickness = new Thickness(r.StrokeThickness, r.StrokeThickness, 0, 0),  // 右下不需要
                                                };
                                                mainCanvas.Children.Add(ad4);
                                                Canvas.SetZIndex(ad4, zIndex);
                                                Canvas.SetLeft(ad4, left + r.Width - adornerWidth / 2 - r.StrokeThickness);
                                                Canvas.SetTop(ad4, top + r.Height - adornerWidth / 2 - r.StrokeThickness);

#if DEBUG
                                                ad4.Background = System.Windows.Media.Brushes.Red;
#endif

                                            }
                                            break;
                                        }
                                    case AreaLocation.BarArea:
                                        {
                                            var area = page.GetArea(bw.LocateAreaIndex, bw.LocateAreasCount > 1);
                                            r.Width = area.Width;
                                            r.Height = area.Height;
                                            Canvas.SetLeft(r, area.X);
                                            Canvas.SetTop(r, area.Y);
                                            break;
                                        }
                                }
                            }
                            break;
                        }
                    case "ReporterSlashLineWidget":
                        {
                            var sw = widget as ReporterSlashLineWidget;
                            if (sw == null) break;

                            var strokeBrush = BrushManager.GetBrush(sw.LineColor);
                            if (strokeBrush == null)
                                strokeBrush = defLineColor;

                            var strokeThickness = sw.LineWidth.HasValue ? sw.LineWidth.Value : rd.DefaultLineWidth;

                            var dashStyle = GetDashStyle(sw.LineDashType);
                            if (dashStyle == null)
                            {
                                dashStyle = defLineDashType;
                            }

                            var halfOfLineWidth = sw.LineWidth.HasValue ? sw.LineWidth.Value : rd.DefaultLineWidth;

                            System.Windows.Shapes.Line line;

                            switch (sw.Direction)
                            {
                                case SlashLineDirection.LeftTopToRightBottom:
                                    {
                                        // 反斜线，左上到右下
                                        line = new System.Windows.Shapes.Line()
                                        {

                                            Stroke = strokeBrush,
                                            StrokeThickness = strokeThickness,
                                            StrokeDashArray = dashStyle,
                                            StrokeDashCap = PenLineCap.Round,             // 会影响点、划线(斜线应为Round)
                                            StrokeEndLineCap = PenLineCap.Round,
                                            StrokeStartLineCap = PenLineCap.Round,
                                            StrokeLineJoin = PenLineJoin.Round,
                                            SnapsToDevicePixels = true
                                        };

                                        switch (sw.Area)
                                        {
                                            case AreaLocation.CentralArea:
                                                {
                                                    line.X1 = page.LeftTopPoint.X + (sw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth) + halfOfLineWidth;
                                                    line.Y1 = page.LeftTopPoint.Y + (sw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth) + halfOfLineWidth;
                                                    line.X2 = page.LeftTopPoint.X + (sw.ColumnIndex + sw.ColumnsCount - 1) * (page.CellWidth + rd.AssistLinesWidth);
                                                    line.Y2 = page.LeftTopPoint.Y + (sw.RowIndex - 1 + sw.RowsCount) * (page.CellHeight + rd.AssistLinesWidth);
                                                    break;
                                                }
                                            case AreaLocation.BarArea:
                                                {
                                                    var area = page.GetArea(sw.LocateAreaIndex, sw.LocateAreasCount > 1);
                                                    line.X1 = area.X;
                                                    line.Y1 = area.Y;
                                                    line.X2 = area.Right;
                                                    line.Y2 = area.Bottom;
                                                    break;
                                                }
                                        }
                                        break;
                                    }
                                default:
                                    {
                                        // 斜线，右上到左下
                                        line = new System.Windows.Shapes.Line()
                                        {

                                            Stroke = strokeBrush,
                                            StrokeThickness = strokeThickness,
                                            StrokeDashArray = dashStyle,
                                            StrokeDashCap = PenLineCap.Round,             // 会影响点、划线(斜线应为Round)
                                            SnapsToDevicePixels = true
                                        };
                                        switch (sw.Area)
                                        {
                                            case AreaLocation.CentralArea:
                                                {
                                                    line.X1 = page.LeftTopPoint.X + (sw.ColumnIndex + sw.ColumnsCount - 1) * (page.CellWidth + rd.AssistLinesWidth);
                                                    line.Y1 = page.LeftTopPoint.Y + (sw.RowIndex - 1) * (page.CellHeight + rd.AssistLinesWidth) + halfOfLineWidth;
                                                    line.X2 = page.LeftTopPoint.X + (sw.ColumnIndex - 1) * (page.CellWidth + rd.AssistLinesWidth) + halfOfLineWidth;
                                                    line.Y2 = page.LeftTopPoint.Y + (sw.RowIndex - 1 + sw.RowsCount) * (page.CellHeight + rd.AssistLinesWidth);
                                                    break;
                                                }
                                            case AreaLocation.BarArea:
                                                {
                                                    var area = page.GetArea(sw.LocateAreaIndex, sw.LocateAreasCount > 1);
                                                    line.X1 = area.Right;
                                                    line.Y1 = area.Top;
                                                    line.X2 = area.Left;
                                                    line.Y2 = area.Bottom;
                                                    break;
                                                }
                                        }
                                        break;
                                    }
                            }
                            mainCanvas.Children.Add(line);
                            Canvas.SetZIndex(line, AdornerWidgetsZIndex);
                            break;
                        }
                    case "ReporterImageWidget":
                        {
                            var iw = widget as ReporterImageWidget;
                            if (iw == null) break;

                            var background = BrushManager.GetBrush(iw.Background);
                            if (background == null)
                                background = defBackground;

                            var borderWidth = iw.BorderWidth.HasValue ? iw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(iw.BorderColor) ? rd.DefaultBorderColor : iw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Background = background,
                                BorderBrush = borderColor,
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var verticalAlignment = iw.VerticalAlignment.HasValue ? iw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var img = new Image()
                            {
                                SnapsToDevicePixels = true
                            };

                            var viewbox = new Viewbox()
                            {
                                SnapsToDevicePixels = true,
                                VerticalAlignment = verticalAlignment,
                            };

                            var imgFilePath = iw.Text;
                            if (System.IO.File.Exists(imgFilePath))
                            {
                                img.Source = Globals.LoadBitmapImageFromFile(imgFilePath);
                            }
                            else
                            {
                                try
                                {
                                    var newPath = System.IO.Path.Combine(defaultImagesPath, imgFilePath);
                                    if (File.Exists(newPath))
                                    {
                                        img.Source = Globals.LoadBitmapImageFromFile(newPath);
                                    }
                                    else { img.Source = noImageSource; }
                                }
                                catch
                                {
                                    img.Source = noImageSource;
                                }
                            }

                            viewbox.Child = img;
                            outborder.Child = viewbox;
                            mainCanvas.Children.Add(outborder);

                            switch (iw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = iw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = iw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth; // 本部件尾列不需要加辅助线宽
                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);
                                        Canvas.SetLeft(outborder, page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (iw.ColumnIndex - 1) + rd.AssistLinesWidth);
                                        Canvas.SetTop(outborder, page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (iw.RowIndex - 1) + rd.AssistLinesWidth);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(iw.LocateAreaIndex, iw.LocateAreasCount > 1);
                                        outborder.Width = area.Width;
                                        outborder.Height = area.Height;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }
                            break;
                        }
                    case "ReporterImageFieldWidget":
                        {
                            var iw = widget as ReporterImageFieldWidget;
                            if (iw == null) break;

                            var background = BrushManager.GetBrush(iw.Background);
                            if (background == null)
                                background = defBackground;

                            var borderWidth = iw.BorderWidth.HasValue ? iw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(iw.BorderColor) ? rd.DefaultBorderColor : iw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Height = iw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth,  // 本部件尾行不需要加辅助线高
                                Width = iw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth, // 本部件尾列不需要加辅助线宽
                                Background = background,
                                BorderBrush = borderColor,
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var verticalAlignment = iw.VerticalAlignment.HasValue ? iw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var img = new FieldImage()
                            {
                                FieldName = iw.FieldName,
                                SnapsToDevicePixels = true
                            };

                            var viewbox = new Viewbox()
                            {
                                SnapsToDevicePixels = true,
                                VerticalAlignment = verticalAlignment,
                            };

                            var imgFilePath = iw.Text;
                            if (System.IO.File.Exists(imgFilePath))
                            {
                                img.Source = Globals.LoadBitmapImageFromFile(imgFilePath);
                            }
                            else
                            {
                                try
                                {
                                    var newPath = System.IO.Path.Combine(defaultFieldImagesPath, imgFilePath);
                                    if (File.Exists(newPath))
                                    {
                                        img.Source = Globals.LoadBitmapImageFromFile(newPath);
                                    }
                                    else { img.Source = noImageSource; }
                                }
                                catch
                                {
                                    img.Source = noImageSource;
                                }
                            }

                            viewbox.Child = img;
                            outborder.Child = viewbox;
                            mainCanvas.Children.Add(outborder);

                            switch (iw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = iw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = iw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾列不需要加辅助线宽

                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);
                                        Canvas.SetLeft(outborder, page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (iw.ColumnIndex - 1) + rd.AssistLinesWidth);
                                        Canvas.SetTop(outborder, page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (iw.RowIndex - 1) + rd.AssistLinesWidth);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(iw.LocateAreaIndex, iw.LocateAreasCount > 1);
                                        outborder.Width = area.Width;
                                        outborder.Height = area.Height;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }

                            fieldElements.Add(img);  // 无论是在边栏还是版芯，都要支持填充。
                            break;
                        }
                    case "ReporterQrCoderWidget":
                        {
                            var qw = widget as ReporterQrCoderWidget;
                            if (qw == null) break;

                            var background = BrushManager.GetBrush(qw.Background);
                            if (background == null)
                                background = defBackground;

                            var borderWidth = qw.BorderWidth.HasValue ? qw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(qw.BorderColor) ? rd.DefaultBorderColor : qw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Background = background,
                                BorderBrush = borderColor,
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var verticalAlignment = qw.VerticalAlignment.HasValue ? qw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var img = new Image()
                            {
                                Height = qw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth,  // 本部件尾行不需要加辅助线高
                                Width = qw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth, // 本部件尾列不需要加辅助线宽
                                Margin = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var viewbox = new Viewbox()
                            {
                                SnapsToDevicePixels = true,
                                VerticalAlignment = verticalAlignment,
                            };

                            img.Source = BuildCodeImage(qw.Text, page.CellWidth * qw.ColumnsCount, page.CellHeight * qw.RowsCount);

                            viewbox.Child = img;
                            outborder.Child = viewbox;
                            mainCanvas.Children.Add(outborder);

                            switch (qw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = qw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = qw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾列不需要加辅助线宽

                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);
                                        Canvas.SetLeft(outborder, page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (qw.ColumnIndex - 1) + rd.AssistLinesWidth);
                                        Canvas.SetTop(outborder, page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (qw.RowIndex - 1) + rd.AssistLinesWidth);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(qw.LocateAreaIndex, qw.LocateAreasCount > 1);
                                        outborder.Width = area.Width;
                                        outborder.Height = area.Height;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }
                            break;
                        }
                    case "ReporterQrCoderFieldWidget":
                        {
                            var qw = widget as ReporterQrCoderFieldWidget;
                            if (qw == null) break;

                            var background = BrushManager.GetBrush(qw.Background);
                            if (background == null)
                                background = defBackground;

                            var borderWidth = qw.BorderWidth.HasValue ? qw.BorderWidth.Value : rd.DefaultBorderWidth;
                            if (borderWidth < 0) borderWidth = 0;
                            var max = Math.Min(page.CellHeight, page.CellWidth) / 2;
                            if (borderWidth > max)
                            {
                                borderWidth = (int)max;
                            }

                            var borderColor = BrushManager.GetBrush(string.IsNullOrEmpty(qw.BorderColor) ? rd.DefaultBorderColor : qw.BorderColor.Trim());
                            if (borderColor == null) borderColor = System.Windows.Media.Brushes.Transparent;

                            var outborder = new System.Windows.Controls.Border()
                            {
                                Background = background,
                                BorderBrush = borderColor,
                                BorderThickness = new Thickness(borderWidth),
                                SnapsToDevicePixels = true
                            };

                            var verticalAlignment = qw.VerticalAlignment.HasValue ? qw.VerticalAlignment.Value : rd.DefaultVerticalAlignment;

                            var img = new FieldQrCoder()
                            {
                                FieldName = qw.FieldName,
                                Height = qw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth,  // 本部件尾行不需要加辅助线高
                                Width = qw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth, // 本部件尾列不需要加辅助线宽
                                SnapsToDevicePixels = true
                            };

                            var viewbox = new Viewbox()
                            {
                                SnapsToDevicePixels = true,
                                VerticalAlignment = verticalAlignment,
                            };

                            img.Source = BuildCodeImage(qw.Text, page.CellWidth * qw.ColumnsCount, page.CellHeight * qw.RowsCount);

                            viewbox.Child = img;
                            outborder.Child = viewbox;
                            mainCanvas.Children.Add(outborder);

                            switch (qw.Area)
                            {
                                case AreaLocation.CentralArea:
                                    {
                                        outborder.Height = qw.RowsCount * (page.CellHeight + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾行不需要加辅助线高
                                        outborder.Width = qw.ColumnsCount * (page.CellWidth + rd.AssistLinesWidth) - rd.AssistLinesWidth;  // 本部件尾列不需要加辅助线宽

                                        Canvas.SetZIndex(outborder, ContentWidgetsZIndex);
                                        Canvas.SetLeft(outborder, page.LeftTopPoint.X + (page.CellWidth + rd.AssistLinesWidth) * (qw.ColumnIndex - 1) + rd.AssistLinesWidth);
                                        Canvas.SetTop(outborder, page.LeftTopPoint.Y + (page.CellHeight + rd.AssistLinesWidth) * (qw.RowIndex - 1) + rd.AssistLinesWidth);
                                        break;
                                    }
                                case AreaLocation.BarArea:
                                    {
                                        var area = page.GetArea(qw.LocateAreaIndex, qw.LocateAreasCount > 1);
                                        outborder.Width = area.Width;
                                        outborder.Height = area.Height;

                                        Canvas.SetLeft(outborder, area.X);
                                        Canvas.SetTop(outborder, area.Y);
                                        break;
                                    }
                            }

                            fieldElements.Add(img);  // 无论在边栏还是在版芯，都应支持填充。
                            break;
                        }
                }
            }

            idWidgetAreasText = idWidgetAreas.ToString();
            questionWidgetAreasText = questionWidgetAreas.ToString();
            answerBoxBorderWidthRateText = answerSheetBoxBorderWidthRate.ToString();
            answerBoxBorderHeightRateText = answerSheetBoxBorderHeightRate.ToString();


#if DEBUG
            LMessageBox.ShowInfomation("idWidgetAreasText:" + idWidgetAreasText + "\r\n\r\nquestionWidgetAreasText:" + questionWidgetAreasText);
#endif
        }

        private static bool GetPageNumberText(string templateText, int groupCount, int groupIndex, int groupPagesCount, int pageNumber, out string pageNumText)
        {
            if (string.IsNullOrWhiteSpace(templateText))
            {
                pageNumText = "";
                return false;
            }

            templateText = ChinesePinYin.FatAlphaAndNumbersToThin(templateText);

            var reg1 = new Regex(@"(?<=[\{｛][ 　\t]*)[xX](?=[ 　\t]*[\}｝])");   // 只有数字
            var match1 = reg1.Match(templateText);
            if (match1.Success)
            {
                if (match1.Value == "x")
                {
                    // 半角
                    pageNumText = (groupIndex * groupPagesCount + pageNumber).ToString();
                    return true;
                }
                else if (match1.Value == "X")
                {
                    pageNumText = ChinesePinYin.ThinAlphaAndNumbersToFat((groupIndex * groupPagesCount + pageNumber).ToString());
                    return true;
                }
            }

            var reg2 = new Regex(@"(?<=[\{｛][ 　\t]*[\-－—][ 　\t]*)[xX](?=[\-－—][ 　\t]*[\}｝])");
            var match2 = reg2.Match(templateText);
            if (match2.Success)
            {
                if (match2.Value == "x")
                {
                    // 半角
                    pageNumText = "- " + (groupIndex * groupPagesCount + pageNumber).ToString() + " -";
                    return true;
                }
                else if (match2.Value == "X")
                {
                    pageNumText = "—" + ChinesePinYin.ThinAlphaAndNumbersToFat((groupIndex * groupPagesCount + pageNumber).ToString()) + "—";
                    return true;
                }
            }

            var reg3 = new Regex(@"(?<=[\{｛][ 　\t]*第[ 　\t]*)[xX](?=[ 　\t]*页[ 　\t]*[\}｝])");
            var match3 = reg3.Match(templateText);
            if (match3.Success)
            {
                if (match3.Value == "x")
                {
                    // 半角
                    pageNumText = "第 " + (groupIndex * groupPagesCount + pageNumber).ToString() + " 页";
                    return true;
                }
                else if (match3.Value == "X")
                {
                    pageNumText = "第 " + ChinesePinYin.ThinAlphaAndNumbersToFat((groupIndex * groupPagesCount + pageNumber).ToString()) + " 页";
                    return true;
                }
            }

            var reg4 = new Regex(@"(?<=[\{｛][ 　\t]*第[ 　\t]*)[xX](?=[ 　\t]*页[ 　\t]*[,，][ 　\t]*共[ 　\t]*[yY][ 　\t]*页[ 　\t]*[\}｝])");
            var match4 = reg4.Match(templateText);
            if (match4.Success)
            {
                if (match4.Value == "x")
                {
                    // 半角
                    pageNumText = "第 " + (groupIndex * groupPagesCount + pageNumber).ToString() + " 页，共 " + (groupCount * groupPagesCount).ToString() + " 页";
                    return true;
                }
                else if (match4.Value == "X")
                {
                    pageNumText = "第 " + ChinesePinYin.ThinAlphaAndNumbersToFat((groupIndex * groupPagesCount + pageNumber).ToString()) + " 页，共 " + ChinesePinYin.ThinAlphaAndNumbersToFat((groupCount * groupPagesCount).ToString()) + " 页";
                    return true;
                }
            }

            pageNumText = "";
            return false;
        }

        public static BitmapImage BuildCodeImage(string text, double width, double height)
        {
            if (string.IsNullOrEmpty(text)) return null;
            try
            {
                var match = RegCodeImageType.Match(text);

                if (match.Success == false)
                {
                    var qrImg = QRBuilder.BuildQrCodeBitmap(text, QRCoder.QRCodeGenerator.ECCLevel.Q, null,
                        (int)Math.Min(width, height), true, false);  // 带底色好
                                                                     // qrImg.Save(@"d:\aaa\testqrcodeImage.png");
                    return ConvertBitmapToBitmapImage(qrImg);
                }

                switch (match.Value.Trim(new char[] { ' ', '　', '\t', '[', ']', '【', '】', '［', '］' }).ToLower())
                {
                    case "39":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成39条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "128":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成128条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "matrix":
                    case "矩阵":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成矩阵条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "pdf417":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成矩阵条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "ean13":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成ean13条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "aztec":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成aztec条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "codabar":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成codabar条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "code93":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成code93条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "ean8":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成ean8条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "itf":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成itf条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "upca":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成upca条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "upce":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成upce条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    case "plessey":
                        {
                            return ConvertBitmapToBitmapImage(QRBuilder.生成plessey条形码(text.Substring(match.Length), (int)width, (int)height));
                        }
                    default:
                        {
                            var qrImg = QRBuilder.BuildQrCodeBitmap(text, QRCoder.QRCodeGenerator.ECCLevel.Q, null,
                            (int)Math.Min(width, height), true, false);  // 带底色好
                                                                         // qrImg.Save(@"d:\aaa\testqrcodeImage.png");
                            return ConvertBitmapToBitmapImage(qrImg);
                        }
                }
            }
            catch
            {
                return null;
            }
        }

        public static BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
                memory.Position = 0;
                BitmapImage bitmapimage = new BitmapImage();
                bitmapimage.BeginInit();
                bitmapimage.StreamSource = memory;
                bitmapimage.CacheOption = BitmapCacheOption.OnLoad; // 缓存选项可以提高性能
                bitmapimage.EndInit();
                bitmapimage.Freeze(); // 防止后续修改，提高性能
                return bitmapimage;
            }
        }

        private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var reporterScript = editorBase.Text;
            if (string.IsNullOrWhiteSpace(reporterScript) == false)
            {
                var doc = new FixedDocument();

                fieldUiElements = BuildReporterByScript(0, doc, reporterScript, out ReporterDefinition rd, ckxPreviewWithAssistLines.IsChecked == true);

                if (fieldUiElements != null && fieldUiElements.Count > 0)
                {
                    if (dataGrid.ItemsSource != null)
                    {
                        FillSelectedItemToReporter(0, fieldUiElements);
                    }
                }

                mainDocumentViewer.Document = doc;
            }

            try
            {
                var btext = App.WorkspaceConfigManager.Get("RememberReportDataSource");
                if (btext != null)
                {
                    if (bool.TryParse(btext, out bool result))
                    {
                        this.rememberPastedDataAsTextFile = result;
                    }
                    else { this.rememberPastedDataAsTextFile = false; }
                }
                else { this.rememberPastedDataAsTextFile = false; }

                if (this.rememberPastedDataAsTextFile)
                {
                    btnRememberData.Background = System.Windows.Media.Brushes.DarkCyan;
                }
                else
                {
                    btnRememberData.Background = System.Windows.Media.Brushes.Transparent;
                }

                var reportDataSourceFilePath = System.IO.Path.Combine(Globals.PathOfWorkspaceAssetsFolder, "$_ReportDataSource~.txt");
                if (this.rememberPastedDataAsTextFile)
                {
                    if (File.Exists(reportDataSourceFilePath))
                    {
                        var textTable = new 文字表(File.ReadAllText(reportDataSourceFilePath));
                        LoadReportAndFillFirstRecord(textTable);

                        if (rdDataGridArea.ActualHeight < 40)
                        {
                            rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
                        }
                    }
                }
                else
                {
                    if (File.Exists(reportDataSourceFilePath))
                    {
                        File.Delete(reportDataSourceFilePath);
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnPreviewReporter_Click(object sender, RoutedEventArgs e)
        {
            // 只预览报表结构，不填充数据记录。
            if (string.IsNullOrWhiteSpace(editorBase.Text))
            {
                LMessageBox.ShowWarning("当前编辑区中未找到报表定义脚本。");
                return;
            }

            var doc = new FixedDocument();

            fieldUiElements = BuildReporterByScript(0, doc, editorBase.Text, out ReporterDefinition rd,
                ckxPreviewWithAssistLines.IsChecked == true);
            // 此处不填充，但还是要准备填充

            mainDocumentViewer.Document = doc;  // 只预览效果

            // 将答题卡定义自动保存到 $_Assets~ 文件夹下（如果存在）
            // 如果不存在答题卡定义，删除可能存在的答题卡定义文件

            string answerSheetFilePath;

            if (string.IsNullOrWhiteSpace(rd.报表名称))
            {
                answerSheetFilePath = Globals.PathOfWorkspaceAssetsFolder + "答题卡定义~.lmasd";
                mainDocumentViewer.Tag = null;
            }
            else
            {
                answerSheetFilePath = Globals.PathOfWorkspaceAssetsFolder + rd.报表名称;
                if (answerSheetFilePath.ToLower().EndsWith(".lmasd") == false)
                {
                    answerSheetFilePath += ".lmasd";
                    mainDocumentViewer.Tag = rd.报表名称;
                }
                else
                {
                    if (answerSheetFilePath.Substring(0, answerSheetFilePath.Length - 4).EndsWith("\\"))
                    {
                        // 说明报表名称只是： .lmasd
                        answerSheetFilePath = Globals.PathOfWorkspaceAssetsFolder + "答题卡定义~.lmasd";
                        mainDocumentViewer.Tag = null;
                    }
                }
            }

            try
            {
                if (File.Exists(answerSheetFilePath))
                {
                    File.Delete(answerSheetFilePath);
                }

                var fi = new FileInfo(answerSheetFilePath); // 如果报表名称设置错误，会抛出异常。

                var pageWidth = "";
                var pageHeight = "";
                var numOrQuesWidgetsCount = 0;
                if (rd.Pages.Count > 0)
                {
                    var page = rd.Pages[0];

                    foreach (var widget in page.Widgets)
                    {
                        if (widget is ReporterCharsRowWdiget || widget is ReporterCharsColumnWdiget)
                        {
                            numOrQuesWidgetsCount++;
                        }
                    }

                    if (numOrQuesWidgetsCount > 0)
                    {
                        // 答题卡的行、列数，应是最大的那个外框的行列数
                        int maxw = 0;
                        int maxh = 0;
                        foreach (var widget in page.Widgets)
                        {
                            var ob = widget as ReporterBoxWidget;
                            if (ob == null || ob.BackgroundLayer) continue;

                            var borderWidth = ob.BoxLineWidth.HasValue ? ob.BoxLineWidth.Value : rd.DefaultBoxLineWidth;

                            maxw = Math.Max(maxw, ob.ColumnsCount);
                            maxh = Math.Max(maxh, ob.RowsCount);
                        }

                        if (maxw > 0 && maxh > 0)
                        {
                            pageWidth = $"答题卡行数>：{maxh}\r\n";
                            pageHeight = $"答题卡列数>：{maxw}\r\n";
                        }
                        File.WriteAllText(answerSheetFilePath, $"答题卡宽高比>：{(double)maxw / (double)maxh}\r\n边框宽度比>：{this.AnswerBoxBorderWidthRate}\r\n边框高度比>：{this.AnswerBoxBorderHeightRate}\r\n\r\n{pageWidth}{pageHeight}" + this.IdWidgetAreasText + "\r\n\r\n" + this.QuestionWidgetAreasText);
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning("无法输出新版答题卡定义文件。错误信息如下：\r\n" + ex.Message);
            }

        }

        private void btnSaveReporterScript_Click(object sender, RoutedEventArgs e)
        {
            SaveReporterScript();
        }
        private void btnSaveReporterScriptAs_Click(object sender, RoutedEventArgs e)
        {
            SaveReporterScriptAs();
        }

        public bool SaveReporterScriptAs()
        {
            try
            {
                if (Directory.Exists(Globals.PathOfWorkspaceAssetsFolder) == false)
                {
                    Directory.CreateDirectory(Globals.PathOfWorkspaceAssetsFolder);
                }

                // 另选路径
                Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog() { Title = Globals.AppName + " - 请设置文件名", };
                sfd.InitialDirectory = Globals.PathOfWorkspaceAssetsFolder;
                sfd.Filter = "报表脚本(*.txt)|*.txt";
                sfd.FilterIndex = 0;
                if (mainDocumentViewer.Tag == null)
                {
                    sfd.FileName = "新报表脚本.txt";
                }
                else
                {
                    var tag = mainDocumentViewer.Tag.ToString();
                    if (string.IsNullOrWhiteSpace(tag) || tag.ToLower() == ".txt")
                    {
                        sfd.FileName = "新报表脚本.txt";
                    }
                    else
                    {
                        sfd.FileName = tag;
                    }
                }

                if (sfd.ShowDialog() != true) return false;

                File.WriteAllText(sfd.FileName, editorBase.Text);
                editorBase.IsModified = false;
                tbMsg.ToolTip = tbMsg.Text =
                    scriptFullPath = sfd.FileName;
                App.WorkspaceConfigManager.Set("ReporterScriptFilePath", scriptFullPath);
                return true;
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
                return false;
            }
        }

        public bool SaveReporterScript()
        {
            try
            {
                try
                {
                    var fi = new FileInfo(scriptFullPath);

                    File.WriteAllText(fi.FullName, editorBase.Text);
                    editorBase.IsModified = false;
                    tbMsg.ToolTip = tbMsg.Text =
                        scriptFullPath = fi.FullName;
                    App.WorkspaceConfigManager.Set("ReporterScriptFilePath", scriptFullPath);
                    return true;
                }
                catch
                {
                    return SaveReporterScriptAs();
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
                return false;
            }
        }

        private void btnLoadReporterScript_Click(object sender, RoutedEventArgs e)
        {
            if (editorBase.IsModified)
            {
                var answer = LMessageBox.Show("当前脚本编辑器器中的文本已被修改，需要先保存吗？", Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Question, "", null, this);
                if (answer == MessageBoxResult.Yes)
                {
                    if (SaveReporterScript() == false) return;
                }
                else if (answer == MessageBoxResult.Cancel) return;
            }

            if (Directory.Exists(Globals.PathOfWorkspaceAssetsFolder) == false)
            {
                Directory.CreateDirectory(Globals.PathOfWorkspaceAssetsFolder);
            }

            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.InitialDirectory = Globals.PathOfWorkspaceAssetsFolder;
            ofd.Multiselect = false;
            ofd.Title = Globals.AppName + "请选择要打开的文件：";
            ofd.Filter = "报表脚本(*.txt)|*.txt";
            ofd.FilterIndex = 0;

            var result = ofd.ShowDialog();
            if (result != true) return;

            if (File.Exists(ofd.FileName))
            {
                editorBase.Text = File.ReadAllText(ofd.FileName);
                editorBase.IsModified = false;
                tbMsg.ToolTip = tbMsg.ToolTip =
                    scriptFullPath = ofd.FileName;
                App.WorkspaceConfigManager.Set("ReporterScriptFilePath", scriptFullPath);
            }
        }

        private void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (dataGrid.SelectedItem == null) return;

            // 先创建报表模板
            if (fieldUiElements == null)
            {
                var doc = new FixedDocument();  // 把 doc 拿到外部，是为了重复添加多张报表到同一文档！
                fieldUiElements = BuildReporterByScript(0, doc, editorBase.Text, out ReporterDefinition rd,
                    ckxPreviewWithAssistLines.IsChecked == true);
                mainDocumentViewer.Document = doc;
            }

            // 向报表模板中填充

            if (fieldUiElements == null || fieldUiElements.Count <= 0)
            {
                // LMessageBox.ShowWarning("");
                tbMsg.Text = "当前报表中没有可供填充的字段，未填充。";
                return;
            }

            FillSelectedItemToReporter(dataGrid.SelectedIndex, fieldUiElements);
        }

        private string defaultImagesPath = "";

        private string defaultFieldImagesPath = "";

        public static string[] GetCompositeFieldInfos(string txt)
        {
            if (string.IsNullOrWhiteSpace(txt)) return null;

            txt = txt.Replace("\\,", "<!--半角逗号-->").Replace("\\，", "<!--全角逗号-->")
                     .Replace("\\\"", "<!--半角双引号-->").Replace("\\“", "<!--全角左双引号-->").Replace("\\”", "<!--全角右双引号-->")
                     .Replace("\\{", "<!--半角左花括号-->").Replace("\\｛", "<!--全角左花括号-->")
                     .Replace("\\}", "<!--半角右花括号-->").Replace("\\｝", "<!--全角右花括号-->")   // 复合字段需要对这些字符进行转义
                     .Replace("\\[", "<!--半角左方括号-->").Replace("\\［", "<!--全角左方括号-->")   // 方括号准备用来转换逻辑值为（■是 □否）这样的样式 
                     .Replace("\\]", "<!--半角右方括号-->").Replace("\\］", "<!--全角右方括号-->")
                     .Replace("\\【", "<!--实心左方括号-->").Replace("\\】", "<!--实心右方括号-->")
                     .Replace("\\(", "<!--半角左圆括号-->").Replace("\\（", "<!--全角左圆括号-->")   // 圆括号暂时未使用  2025.5.18
                     .Replace("\\)", "<!--半角右圆括号-->").Replace("\\）", "<!--全角右圆括号-->")
                     ;

            txt = txt.Trim();
            var reg = new Regex(@"^[\{｛].*([\}｝]|$)");
            var reg2 = new Regex(@"^[""“].*[""”]");
            var match = reg.Match(txt.Trim());
            if (match.Success)
            {
                var spans = txt.Trim(new char[] { ' ', '　', '\t', '{', '}', '｛', '｝' })
                               .Split(new char[] { ',', '，' }, StringSplitOptions.RemoveEmptyEntries);
                if (spans.Length < 1) return null;  // 说明不是复合字段（复合字段只有一个）

                foreach (var span in spans)
                {
                    var match2 = reg2.Match(span);
                    if (match2.Success == false)
                    {
                        return spans;  // 如果有非静态文本（引号括起来的），说明至少存在一个字段
                    }
                }

                return null;  // 说明不存在字段
            }
            else return null; // 非复合字段
        }

        /// <summary>
        /// 字段转 ■字段名 或 □字段名
        /// </summary>
        private static Regex regCheckBoxWithFieldName = new Regex(@"^[ 　\t]*[\[［【](([ 　\t]*)|(.{1,}[|｜].{1,}))[\]］】][ 　\t]*");

        public static Regex RegCheckBoxWithFieldName { get => regCheckBoxWithFieldName; }

        /// <summary>
        /// 字段转 ■是 □否 或 □是 ■否
        /// </summary>
        private static Regex regCheckBoxWithBoolValue = new Regex(@"(?<=(^[ 　\t]*)[\[［【]).*(?=([\]］】][ 　\t]*$))");

        public static Regex RegCheckBoxWithBoolValue { get => regCheckBoxWithBoolValue; }

        #region 上次生成答题卡（报表）时得到的考试号、试题位置信息
        public string IdWidgetAreasText_inBar { get; private set; } = "";
        public string QuestionWidgetAreasText_inBar { get; private set; } = "";
        public string IdWidgetAreasText { get; private set; } = "";
        public string QuestionWidgetAreasText { get; private set; } = "";
        public string AnswerBoxBorderWidthRate { get; private set; } = "";
        public string AnswerBoxBorderHeightRate { get; private set; } = "";
        #endregion

        private void FillSelectedItemToReporter(int dataGridItemIndex, List<IFieldUIElement> fieldUiElements)
        {
            try
            {
                if (dataContextTable == null || dataContextTable.文字表体行集 == null || dataContextTable.文字表体行集.Count <= 0) return;

                if (dataGridItemIndex < 0 || dataGridItemIndex >= dataContextTable.文字表体行集.Count) return;

                var datarow = dataContextTable.文字表体行集[dataGridItemIndex];
                if (datarow == null) return;

                if (dataContextTable.文字表头行集 != null && dataContextTable.文字表头行集.Count > 0 &&
                    dataContextTable.文字表头行集[0].文字表单元格集 != null &&
                    dataContextTable.文字表头行集[0].文字表单元格集.Count > 0)
                {
                    var headRow = dataContextTable.文字表头行集[0];

                    if (dataContextTable.文字表体行集 != null && dataContextTable.文字表体行集.Count > 0)
                    {
                        // 两层循环
                        foreach (var ui in fieldUiElements)
                        {
                            var hasFieldValue = false;
                            var compositeFields = GetCompositeFieldInfos(ui.FieldName);

                            if (compositeFields == null)
                            {
                                // 为部件设置的是单字段名
                                for (int i = 0; i < headRow.文字表单元格集.Count; i++)
                                {
                                    var headValue = headRow.文字表单元格集[i].文字表单元格文本.Trim().ToLower();
                                    if (string.IsNullOrWhiteSpace(headValue)) continue;

                                    var cellValue = datarow.文字表单元格集[i].文字表单元格文本;

                                    if (string.IsNullOrWhiteSpace(ui.FieldName)) continue;

                                    if (headValue == ui.FieldName.Trim().ToLower())
                                    {
                                        ui.SetFieldValue(cellValue, defaultFieldImagesPath);
                                        hasFieldValue = true;
                                    }
                                }
                            }
                            else
                            {
                                // 为部件设置的是复合字段名
                                var sb = new StringBuilder();

                                var trueValueParttern = ReporterDefinition.InnerTrueValuesText;
                                if (string.IsNullOrWhiteSpace(ReporterDefinition.DefaultTrueValuesText) == false)
                                {
                                    trueValueParttern += ReporterDefinition.DefaultTrueValuesText;
                                }

                                var falseValueParttern = ReporterDefinition.InnerFalseValuesText;
                                if (string.IsNullOrWhiteSpace(ReporterDefinition.DefaultFalseValuesText) == false)
                                {
                                    falseValueParttern += ReporterDefinition.DefaultFalseValuesText;
                                }

                                foreach (var cField in compositeFields)
                                {
                                    var cFieldTxt = cField.Trim();
                                    if ((cFieldTxt.StartsWith("\"") || cFieldTxt.StartsWith("“")) &&
                                       (cFieldTxt.EndsWith("\"") || cFieldTxt.EndsWith("”")))
                                    {
                                        if (cField.Length <= 1)
                                        {
                                            LMessageBox.Show($"复合字段书写错误。这可能是因为复合字段文本中误用了逗号却未前面加反斜杠 \\ 字符进行转义导致的。请检查：\r\n\r\n{ui.FieldName}");

                                            return;  // 复合字段中逗号未转义会出问题。
                                        }

                                        sb.Append(cFieldTxt.Substring(1, cFieldTxt.Length - 2)
                                                           .Replace("<!--半角逗号-->", ",").Replace("<!--全角逗号-->", "，")
                                                           .Replace("<!--半角双引号-->", "\"").Replace("<!--全角左双引号-->", "“").Replace("<!--全角右双引号-->", "”")
                                                           .Replace("<!--半角左花括号-->", "{").Replace("<!--全角左花括号-->", "｛")
                                                           .Replace("<!--半角右花括号-->", "}").Replace("<!--全角右花括号-->", "｝")  // 复合字段需要对这些字符进行转义
                                                           .Replace("<!--半角左方括号-->", "[").Replace("<!--全角左方括号-->", "［")  // 方括号准备用来转换逻辑值为（■是 □否）这样的样式
                                                           .Replace("<!--半角右方括号-->", "]").Replace("<!--全角右方括号-->", "］")
                                                           .Replace("<!--实心左方括号-->", "【").Replace("<!--实心右方括号-->", "】")
                                                           .Replace("<!--半角左圆括号-->", "(").Replace("<!--全角左圆括号-->", "（")  // 圆括号暂时未使用 2025.5.18
                                                           .Replace("<!--半角右圆括号-->", ")").Replace("<!--全角右圆括号-->", "）")

                                                           );  // 这里不用 Trim()，目的是保留可能需要的引号。
                                        continue;
                                    }

                                    for (int i = 0; i < headRow.文字表单元格集.Count; i++)
                                    {
                                        var headValue = headRow.文字表单元格集[i].文字表单元格文本.Trim().ToLower();
                                        if (string.IsNullOrWhiteSpace(headValue)) continue;

                                        var cellValue = datarow.文字表单元格集[i].文字表单元格文本;

                                        if (string.IsNullOrWhiteSpace(ui.FieldName)) continue;


                                        // 这时是动态字段
                                        var matchCheckBoxWithFieldName = regCheckBoxWithFieldName.Match(cFieldTxt);
                                        if (matchCheckBoxWithFieldName.Success)
                                        {
                                            var fieldTail = cFieldTxt.Substring(matchCheckBoxWithFieldName.Length).Trim();
                                            if (headValue == fieldTail.ToLower())
                                            {
                                                var tfValues = matchCheckBoxWithFieldName.Value.Trim(new char[] { ' ', '　', '\t', '[', '［', '【', ']', '］', '】' })
                                                                                         .Split(new char[] { '|', '｜' }, StringSplitOptions.RemoveEmptyEntries);
                                                var trueValue = "";
                                                var falseValue = "";
                                                if (tfValues != null && tfValues.Length == 2)
                                                {
                                                    trueValue += tfValues[0];
                                                    falseValue += tfValues[1];
                                                }

                                                if (string.IsNullOrWhiteSpace(cellValue))
                                                {
                                                    // 空值不显示
                                                    //sb.Append("");
                                                    hasFieldValue = true;
                                                    continue;
                                                }

                                                if (trueValueParttern.Contains(cellValue.Trim().ToLower()))
                                                {
                                                    // 真值
                                                    sb.Append("■" + (string.IsNullOrWhiteSpace(trueValue) ? fieldTail : trueValue));
                                                    hasFieldValue = true;
                                                    continue;
                                                }

                                                if (falseValueParttern.Contains(cellValue.Trim().ToLower()))
                                                {
                                                    // 假值
                                                    sb.Append("□" + (string.IsNullOrWhiteSpace(falseValue) ? fieldTail : falseValue));
                                                    hasFieldValue = true;
                                                    continue;
                                                }

                                                sb.Append("Err: " + fieldTail);  // 不能识别的
                                            }
                                        }
                                        else
                                        {
                                            var matchCheckBoxWithBoolValue = regCheckBoxWithBoolValue.Match(cFieldTxt);
                                            if (matchCheckBoxWithBoolValue.Success)
                                            {
                                                if (headValue == cFieldTxt.Trim(new char[] { ' ', '　', '\t', '[', '［', '【', ']', '］', '】' }).ToLower())
                                                {
                                                    var cellValueTxt = cellValue.Trim().ToLower();
                                                    if (string.IsNullOrWhiteSpace(cellValueTxt)) continue; // 空值不显示

                                                    if (trueValueParttern.Contains(cellValueTxt))
                                                    {
                                                        // 真值
                                                        sb.Append("■是　□否");
                                                        hasFieldValue = true;
                                                        continue;
                                                    }

                                                    if (falseValueParttern.Contains(cellValueTxt))
                                                    {
                                                        // 假值
                                                        sb.Append("□是　■否");
                                                        hasFieldValue = true;
                                                        continue;
                                                    }

                                                    sb.Append("Err: " + cellValue);  // 不能识别的
                                                }
                                            }
                                            else
                                            {
                                                if (headValue == cFieldTxt.Trim(new char[] { ' ', '　', '\t', '[', '［', '【', ']', '］', '】' }).ToLower())
                                                {
                                                    sb.Append(cellValue);
                                                    hasFieldValue = true;
                                                }
                                            }
                                        }
                                    }
                                }

                                ui.SetFieldValue(sb.Replace("<br>", "\r\n").Replace("&#13;&#10;", "\r\n").Replace("<LineBreak/>", "\r\n").ToString(), defaultImagesPath);
                            }

                            if (hasFieldValue)
                            {
                                ui.RefreshErrorStatus();
                            }
                            else
                            {
                                ui.RefreshErrorStatus(ErrForeColor);
                            }
                        }

                        tbRecordIndex.Text = $"已填充第 {dataGridItemIndex + 1} 条记录";
                    }
                    else tbRecordIndex.Text = "";
                }
                else tbRecordIndex.Text = "";
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void miUndo_Click(object sender, RoutedEventArgs e)
        {
            if (editorBase.CanUndo) editorBase.Undo();
        }

        private void miRedo_Click(object sender, RoutedEventArgs e)
        {
            if (editorBase.CanRedo) editorBase.Redo();
        }

        private void miCut_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Cut();
        }

        private void miCopy_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Copy();
        }

        private void miPaste_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Paste();
        }

        private void miSelectAll_Click(object sender, RoutedEventArgs e)
        {
            editorBase.SelectAll();
        }

        private void spEnableAutoComplete_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            editorBase.EnableAutoComplete = !editorBase.EnableAutoComplete;
        }

        /// <summary>
        /// 已知：DocumentViewer 中导出的 XPS Document 中 WPF 的 TextBlock 会被转换为其它类型的、适合文档存储的对象。
        /// 再导入 XPS 时，不会重建 WPF TextBlock 对象——因而“导出 XPS 模板，重新载入 XPS 模板并再次填充”是行不通的。
        /// </summary>
        /// <param name="filePath"></param>
        private void LoadXpsFromMemory(string filePath)
        {
            try
            {
                // 1. 读取XPS文件到内存流
                var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                var memoryStream = new MemoryStream();
                fileStream.CopyTo(memoryStream);
                memoryStream.Seek(0, SeekOrigin.Begin);

                // 2. 创建Package对象
                var package = Package.Open(memoryStream, FileMode.Open, FileAccess.Read);
                var uri = new Uri($"memorystream://{filePath}");
                PackageStore.AddPackage(uri, package); // 注册到全局存储

                // 3. 初始化XpsDocument
                using (var xpsDoc = new XpsDocument(
                    package,
                    CompressionOption.NotCompressed,
                    uri.AbsoluteUri))
                {
                    var docSeq = xpsDoc.GetFixedDocumentSequence();
                    mainDocumentViewer.Document = docSeq;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"加载失败: {ex.Message}");
            }
        }

        private void btnOutputXpsDocument_Click(object sender, RoutedEventArgs e)
        {
            var document = mainDocumentViewer.Document as FixedDocument;
            if (document == null)
            {
                LMessageBox.ShowWarning("当前预览区尚未生成报表文档！");
                return;
            }

            try
            {
                SaveFileDialog sfd = new SaveFileDialog() { Title = Globals.AppName + " - 请设置导出文件名", };
                sfd.Filter = "XPS 文档(*.xps)|*.xps";
                sfd.FilterIndex = 0;
                sfd.FileName = "导出报表 " + DateTime.Now.ToShortDateString().Replace("/", "-") + ".xps";
                sfd.InitialDirectory = Globals.PathOfWorkspaceAssetsFolder;

                if (sfd.ShowDialog() != true) return;

                var fi = new FileInfo(sfd.FileName);

                //PrintTicket printTicket = new PrintTicket();
                //printTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4);

                using (XpsDocument xpsDoc = new XpsDocument(fi.FullName, FileAccess.Write))
                {
                    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
                    writer.Write(document); // 将 DocumentViewer 内容写入 XPS 文件
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnFillAllRecords_Click(object sender, RoutedEventArgs e)
        {
            if (dataGrid.Items == null || dataGrid.Items.Count <= 0)
            {
                LMessageBox.ShowWarning("当前数据区域无数据，无法填充！");
                return;
            }

            fieldUiElements = null;  // 重置

            var doc = new FixedDocument();

            for (int i = 0; i < dataGrid.Items.Count; i++)
            {
                var fieldUiElements = BuildReporterByScript(i, doc, editorBase.Text, out ReporterDefinition rd,
                    ckxPreviewWithAssistLines.IsChecked == true);
                if (fieldUiElements == null || fieldUiElements.Count <= 0)
                {
                    mainDocumentViewer.Document = doc;
                    // LMessageBox.ShowWarning("当前生成的报表中未定义字段，无须填充！");
                }

                FillSelectedItemToReporter(i, fieldUiElements);
            }

            mainDocumentViewer.Document = doc;
            tbRecordIndex.Text = $"已填充所有记录";
        }

        /// <summary>
        /// 只有填充单记录才应有值，填充多记录时，此值应为 null。
        /// </summary>
        private List<IFieldUIElement> fieldUiElements = null;

        private void btnPasteExcelDataBlock_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // 即使粘贴的新数据块的字段列表与原有字段列表不同，也不自动将 fieldUiElement 列表重置为 null
                // 这样设计是因为：粘贴的数据可能只是比原有数据多一些内容，没有必要更新报表！

                var excelText = Clipboard.GetText(TextDataFormat.Text);
                if (string.IsNullOrEmpty(excelText)) return;

                var srcText = new StringBuilder();
                int index = excelText.IndexOf("\r\n");
                if (index >= 0)
                {
                    srcText.Append(excelText.Substring(0, index).Replace("\t", "|"));
                    srcText.Append("\r\n|-|");
                    srcText.Append(excelText.Substring(index));

                    var sourceTableText = srcText.ToString().Replace("\t", "|");
                    var textTable = new 文字表(sourceTableText);
                    LoadReportAndFillFirstRecord(textTable);

                    if (rdDataGridArea.ActualHeight < 40)
                    {
                        rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
                    }

                    var reportDataSourceFilePath = System.IO.Path.Combine(Globals.PathOfWorkspaceAssetsFolder, "$_ReportDataSource~.txt");
                    if (rememberPastedDataAsTextFile)
                    {
                        File.WriteAllText(reportDataSourceFilePath, sourceTableText, Encoding.UTF8);
                    }
                    else
                    {
                        if (File.Exists(reportDataSourceFilePath))
                        {
                            File.Delete(reportDataSourceFilePath);
                        }
                    }
                    return;
                }

                LMessageBox.ShowWarning("复制的数据块有特殊格式或剪贴板被其它程序占用，无法粘贴。");
            }
            catch (Exception ex)
            {
                LMessageBox.Show("未能粘贴 Excel 数据块。错误信息如下：\r\n" + ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void btnPaste2DTextTable_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // 即使粘贴的新数据块的字段列表与原有字段列表不同，也不自动将 fieldUiElement 列表重置为 null
                // 这样设计是因为：粘贴的数据可能只是比原有数据多一些内容，没有必要更新报表！

                var srcText = Clipboard.GetText(TextDataFormat.Text);
                if (string.IsNullOrEmpty(srcText)) return;

                var textTable = new 文字表(srcText);
                LoadReportAndFillFirstRecord(textTable);

                if (rdDataGridArea.ActualHeight < 40)
                {
                    rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
                }

                var reportDataSourceFilePath = System.IO.Path.Combine(Globals.PathOfWorkspaceAssetsFolder, "$_ReportDataSource~.txt");
                if (rememberPastedDataAsTextFile)
                {
                    File.WriteAllText(reportDataSourceFilePath, srcText, Encoding.UTF8);
                }
                else
                {
                    if (File.Exists(reportDataSourceFilePath))
                    {
                        File.Delete(reportDataSourceFilePath);
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show("未能粘贴二维文字表数据块。错误信息如下：\r\n" + ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            this.Hide();
            e.Cancel = true;

            // 现在不关闭，只隐藏，只允许打开一个报表设计器实例。
            //if (editorBase.IsModified)
            //{
            //    var answer = LMessageBox.Show("编辑区的报表生成脚本已修改，要保存吗？", Globals.AppName,
            //        MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, "", null, this);
            //    switch (answer)
            //    {
            //        case MessageBoxResult.Yes:
            //            {
            //                if (SaveReporterScript() == false)
            //                {
            //                    return;
            //                }
            //                else this.Hide();

            //                e.Cancel = true;
            //                break;
            //            }
            //        case MessageBoxResult.No:
            //            {
            //                this.Hide();    // 不关闭
            //                e.Cancel = true;
            //                break;
            //            }
            //        case MessageBoxResult.Cancel:
            //            {
            //                e.Cancel = true;
            //                break;
            //            }
            //    }
            //}
            //else
            //{
            //    this.Hide();
            //    e.Cancel = true;
            //}
        }

        private void miAutoWrap_Click(object sender, RoutedEventArgs e)
        {
            editorBase.WordWrap = (miAutoWrap.IsChecked = !miAutoWrap.IsChecked);
        }

        private void btnDataGridArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (rdDataGridArea.ActualHeight > 20)
            {
                rdDataGridArea.Height = new GridLength(0, GridUnitType.Star);
            }
            else rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
        }

        private void btnSwitchEditerAndPreviewer_Click(object sender, RoutedEventArgs e)
        {
            switchEditorAndPreviewer = !switchEditorAndPreviewer;
            SwitchEditorAndPreviewer(switchEditorAndPreviewer);
        }

        private void tbnDockWindow_Click(object sender, RoutedEventArgs e)
        {
            var scaleX = 96.00 / Utils.ScreenDPIHelper.DpiX;
            var scaleY = 96.00 / Utils.ScreenDPIHelper.DpiY;

            var screenWidth = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width * scaleX;
            var screenHeight = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height * scaleY;

            var h = Canvas.GetTop(rectDockWindow);

            if (h > 0)
            {
                // 此时应向下停靠
                this.Width = screenWidth;
                this.Left = 0;
                this.Height = screenHeight / 2;
                this.Top = screenHeight / 2;
                Canvas.SetTop(rectDockWindow, 0);
                tbnDockWindow.ToolTip = "窗口向上停靠";
            }
            else
            {
                // 此时应向上停靠
                this.Width = screenWidth;
                this.Left = 0;
                this.Height = screenHeight / 2;
                this.Top = 0;
                Canvas.SetTop(rectDockWindow, 8);
                tbnDockWindow.ToolTip = "窗口向下停靠";
            }

            if (this.WindowState == WindowState.Maximized)
                this.WindowState = WindowState.Normal;

        }
        // 不要保存、载入 XPS 文件——因为用户的电脑配置可能缺少 Microsoft XPS Document Writer ！



        private void btnTopmost_Click(object sender, RoutedEventArgs e)
        {
            this.Topmost = !this.Topmost;
            if (this.Topmost)
            {
                backgroundGridTopMost.Background = System.Windows.Media.Brushes.DarkCyan;
            }
            else
            {
                backgroundGridTopMost.Background = System.Windows.Media.Brushes.Transparent;
            }
        }

        private void tbnHelp_Click(object sender, RoutedEventArgs e)
        {
            ShowHelpDocumentPage();
        }

        private bool rememberPastedDataAsTextFile = false;

        private void btnRememberData_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (rememberPastedDataAsTextFile)
                {
                    btnRememberData.Background = System.Windows.Media.Brushes.Transparent;
                    rememberPastedDataAsTextFile = false;
                }
                else
                {
                    btnRememberData.Background = System.Windows.Media.Brushes.DarkCyan;
                    rememberPastedDataAsTextFile = true;
                }
                App.WorkspaceConfigManager.Set("RememberReportDataSource", rememberPastedDataAsTextFile.ToString());

                var reportDataSourceFilePath = System.IO.Path.Combine(Globals.PathOfWorkspaceAssetsFolder, "$_ReportDataSource~.txt");
                if (File.Exists(reportDataSourceFilePath) && rememberPastedDataAsTextFile == false)
                {
                    File.Delete(reportDataSourceFilePath);
                }

                miRememberData.IsChecked = rememberPastedDataAsTextFile;
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void Menu_Initialized(object sender, EventArgs e)
        {
            var m = sender as MenuItem;
            if (m == null) return;
            var cm = m.ContextMenu;
            if (cm == null) return;

            cm.Placement = PlacementMode.Bottom;
            cm.PlacementTarget = m;
            m.ContextMenu = null;
            m.Tag = cm;

            cm.Opened += Cm_Opened;
            cm.Closed += Cm_Closed;
            m.PreviewMouseLeftButtonDown += M_PreviewMouseLeftButtonDown;
        }

        private void M_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var m = sender as MenuItem;
            if (m == null) return;
            var cm = m.Tag as ContextMenu;
            if (cm == null) return;

            cm.IsOpen = true;
        }

        private void Cm_Closed(object sender, RoutedEventArgs e)
        {
            var cm = sender as ContextMenu;
            if (cm == null || cm.PlacementTarget == null) return;
            var m = cm.PlacementTarget as MenuItem;
            if (m == null) return;

            m.Background = System.Windows.Media.Brushes.Transparent;
            m.Foreground = System.Windows.Media.Brushes.White;
        }

        private void Cm_Opened(object sender, RoutedEventArgs e)
        {
            var cm = sender as ContextMenu;
            if (cm == null || cm.PlacementTarget == null) return;
            var m = cm.PlacementTarget as MenuItem;
            if (m == null) return;

            m.Background = System.Windows.Media.Brushes.LightGray;
            m.Foreground = System.Windows.Media.Brushes.Black;

            var point = cm.PlacementTarget.PointToScreen(new System.Windows.Point(0, 0));
            //if (Environment.OSVersion.Version.Major > 6 ||
            //    (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1))  // win 8 以上（6.2）
            //{
            cm.HorizontalOffset = 0;  // -cm.ActualWidth + m.ActualWidth + 6;// point.X - splitMenu2.ActualWidth + btnFindHeaders_AppendArrow.ActualWidth;
            cm.VerticalOffset = 0;  // point.Y - 8;
            //}
        }

        private void miOpenReportScriptFile_Click(object sender, RoutedEventArgs e)
        {
            btnLoadReporterScript_Click(sender, e);
        }

        private void miShowHelp_Click(object sender, RoutedEventArgs e)
        {
            ShowHelpDocumentPage();
        }

        private void miClose_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void miShowSpaces_Click(object sender, RoutedEventArgs e)
        {
            miShowSpaces.IsChecked =
                editorBase.Options.ShowSpaces = !editorBase.Options.ShowSpaces;
        }

        private void miShowEndOfLine_Click(object sender, RoutedEventArgs e)
        {
            miShowEndOfLine.IsChecked =
                editorBase.Options.ShowEndOfLine = !editorBase.Options.ShowEndOfLine;
        }

        private void miShowTabs_Click(object sender, RoutedEventArgs e)
        {
            miShowTabs.IsChecked =
                editorBase.Options.ShowTabs = !editorBase.Options.ShowTabs;
        }

        private void miShowLineNumbers_Click(object sender, RoutedEventArgs e)
        {
            miShowLineNumbers.IsChecked =
                editorBase.ShowLineNumbers = !editorBase.ShowLineNumbers;
        }

        private void miCommentSelectedLines_Click(object sender, RoutedEventArgs e)
        {
            editorBase.CommentSelectedLines();
        }

        private void miFindMain_Click(object sender, RoutedEventArgs e)
        {
            Find();
        }

        private void miReplaceMain_Click(object sender, RoutedEventArgs e)
        {
            Replace();
        }

        private void miSwitchFullScreen_Click(object sender, RoutedEventArgs e)
        {
            SwitchFullLayout();
        }

        private void miSwitchPreviewAndEditorArea_Click(object sender, RoutedEventArgs e)
        {
            switchEditorAndPreviewer = !switchEditorAndPreviewer;
            SwitchEditorAndPreviewer(switchEditorAndPreviewer);
        }

        private void miSwitchDataArea_Click(object sender, RoutedEventArgs e)
        {
            if (rdDataGridArea.ActualHeight > 10)
            {
                rdDataGridArea.Height = new GridLength(0, GridUnitType.Star);
            }
            else
            {
                rdDataGridArea.Height = new GridLength(1, GridUnitType.Star);
            }
        }

        private void miSave_Click(object sender, RoutedEventArgs e)
        {
            btnSaveReporterScript_Click(sender, e);
        }

        private void miSaveAs_Click(object sender, RoutedEventArgs e)
        {
            btnSaveReporterScriptAs_Click(sender, e);
        }

        private void miPreviewReport_Click(object sender, RoutedEventArgs e)
        {
            btnPreviewReporter_Click(sender, e);
        }

        private void miPasteExcelDataBlock_Click(object sender, RoutedEventArgs e)
        {
            btnPasteExcelDataBlock_Click(sender, e);
        }

        private void miPaste2DTable_Click(object sender, RoutedEventArgs e)
        {
            btnPaste2DTextTable_Click(sender, e);
        }

        private void miFillAll_Click(object sender, RoutedEventArgs e)
        {
            btnFillAllRecords_Click(sender, e);
        }

        private void miExportXPSFile_Click(object sender, RoutedEventArgs e)
        {
            btnOutputXpsDocument_Click(sender, e);
        }

        private void miShowAssistLines_Click(object sender, RoutedEventArgs e)
        {
            // 此属性要么临时设置，要么在报表生成脚本中指定——不存储在工作区配置文件中。
            var showAssistLine = false;
            if (ckxPreviewWithAssistLines.IsChecked == true)
            {
                showAssistLine = true;
            }

            ckxPreviewWithAssistLines.IsChecked = !showAssistLine;  // 这个要反状态
            miShowAssistLines.IsChecked = !showAssistLine;
        }

        private void ckxPreviewWithAssistLines_Click(object sender, RoutedEventArgs e)
        {
            // 此属性要么临时设置，要么在报表生成脚本中指定——不存储在工作区配置文件中。
            var showAssistLine = false;
            if (ckxPreviewWithAssistLines.IsChecked == true)
            {
                showAssistLine = true;
            }

            miShowAssistLines.IsChecked = showAssistLine;   // 这个不能反状态
        }
        private void miRememberData_Click(object sender, RoutedEventArgs e)
        {
            btnRememberData_Click(sender, e);
        }

        private void mainDocumentViewer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (mainDocumentViewer.ActualWidth < 30)
            {
                btnPreview.Visibility = Visibility.Collapsed;
            }
            else
            {
                btnPreview.Visibility = Visibility.Visible;
            }
        }

        private void miInsertNewText_Click(object sender, RoutedEventArgs e)
        {
            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
            var spanTxt = "\r\n；新文本：（_，_，1，）";

            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
            editorBase.Select(curLine.EndOffset + spanTxt.Length - 1, 0);
        }

        private void miInsertNewFieldText_Click(object sender, RoutedEventArgs e)
        {
            var curLine = editorBase.Document.GetLineByOffset(editorBase.SelectionEnd);
            var spanTxt = "\r\n；新文本字段：（_，_，1，）";

            editorBase.Document.Insert(curLine.EndOffset, spanTxt);
            editorBase.Select(curLine.EndOffset + spanTxt.Length - 1, 0);
        }

        private void miInsertCompsiteBrackts_Click(object sender, RoutedEventArgs e)
        {
            var selTxt = editorBase.Document.GetText(editorBase.SelectionStart, editorBase.SelectionLength);
            var selStart = editorBase.SelectionStart;

            if (string.IsNullOrEmpty(selTxt))
            {
                editorBase.SelectedText = "{}";
                editorBase.Select(selStart + 1, 0);
            }
            else
            {
                editorBase.SelectedText = "{" + selTxt + "}";
                editorBase.Select(selStart + 1 + selTxt.Length, 0);
            }
        }

        private void miInsertCompsiteLogicBrackts_Click(object sender, RoutedEventArgs e)
        {
            var selTxt = editorBase.Document.GetText(editorBase.SelectionStart, editorBase.SelectionLength);
            var selStart = editorBase.SelectionStart;

            if (string.IsNullOrEmpty(selTxt))
            {
                editorBase.SelectedText = "{[]}";
                editorBase.Select(selStart + 2, 0);
            }
            else
            {
                editorBase.SelectedText = "{[" + selTxt + "]}";
                editorBase.Select(selStart + 3 + selTxt.Length, 0);  // 要准备输入另一个字段，所以这里是3
            }
        }

        private void miInsertCompsiteCustomLogicBrackts_Click(object sender, RoutedEventArgs e)
        {
            var selTxt = editorBase.Document.GetText(editorBase.SelectionStart, editorBase.SelectionLength);
            var selStart = editorBase.SelectionStart;

            if (string.IsNullOrEmpty(selTxt))
            {
                editorBase.SelectedText = "{[]}";
                editorBase.Select(selStart + 3, 0);
            }
            else
            {
                editorBase.SelectedText = "{[]" + selTxt + "}";
                editorBase.Select(selStart + 3 + selTxt.Length, 0);
            }
        }

        private void tbMsg_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount != 2) return;

            if (File.Exists(tbMsg.Text))
            {
                try
                {
                    string[] files = new string[1];
                    files[0] = tbMsg.Text;
                    Clipboard.SetData(DataFormats.FileDrop, files);
                }
                catch (Exception ex)
                {
                    LMessageBox.ShowWarning(ex.Message);
                }
            }
        }
    }

    public class QuestionOptionEntry
    {
        public bool IsQuestionNumber { get; set; } = false;

        public string Text { get; set; } = "";
    }

    public class ReportHelpPathInfo
    {
        public string CommandName { get; set; }

        public string HelpRelativePath { get; set; }
    }
}
